2813f6261ac3008e2ba9c34f5681751e32bfb624
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / lib / dbixcsl_common_tests.pm
1 package dbixcsl_common_tests;
2
3 use strict;
4 use warnings;
5
6 use Test::More;
7 use Test::Exception;
8 use DBIx::Class::Schema::Loader;
9 use Class::Unload;
10 use File::Path;
11 use DBI;
12 use Digest::MD5;
13 use File::Find 'find';
14 use Class::Unload ();
15 use DBIx::Class::Schema::Loader::Utils 'dumper_squashed';
16 use List::MoreUtils 'apply';
17 use DBIx::Class::Schema::Loader::Optional::Dependencies ();
18 use namespace::clean;
19
20 use dbixcsl_test_dir qw/$tdir/;
21
22 my $DUMP_DIR = "$tdir/common_dump";
23 rmtree $DUMP_DIR;
24
25 use constant RESCAN_WARNINGS => qr/(?i:loader_test)\d+ has no primary key|^Dumping manual schema|^Schema dump completed|collides with an inherited method|invalidates \d+ active statement|^Bad table or view/;
26
27 sub new {
28     my $class = shift;
29
30     my $self;
31
32     if( ref($_[0]) eq 'HASH') {
33        my $args = shift;
34        $self = { (%$args) };
35     }
36     else {
37        $self = { @_ };
38     }
39
40     # Only MySQL uses this
41     $self->{innodb} ||= '';
42
43     # DB2 and Firebird don't support 'field type NULL'
44     $self->{null} = 'NULL' unless defined $self->{null};
45     
46     $self->{verbose} = $ENV{TEST_VERBOSE} || 0;
47
48     # Optional extra tables and tests
49     $self->{extra} ||= {};
50
51     $self->{basic_date_datatype} ||= 'DATE';
52
53     # Not all DBS do SQL-standard CURRENT_TIMESTAMP
54     $self->{default_function} ||= "current_timestamp";
55     $self->{default_function_def} ||= "timestamp default $self->{default_function}";
56
57     $self = bless $self, $class;
58
59     $self->setup_data_type_tests;
60
61     return $self;
62 }
63
64 sub skip_tests {
65     my ($self, $why) = @_;
66
67     plan skip_all => $why;
68 }
69
70 sub _monikerize {
71     my $name = shift;
72     return 'LoaderTest2X' if $name =~ /^loader_test2$/i;
73     return undef;
74 }
75
76 sub run_tests {
77     my $self = shift;
78
79     my @connect_info;
80
81     if ($self->{dsn}) {
82         push @connect_info, [ @{$self}{qw/dsn user password connect_info_opts/ } ];
83     }
84     else {
85         foreach my $info (@{ $self->{connect_info} || [] }) {
86             push @connect_info, [ @{$info}{qw/dsn user password connect_info_opts/ } ];
87         }
88     }
89     
90     if ($ENV{SCHEMA_LOADER_TESTS_EXTRA_ONLY}) {
91         $self->run_only_extra_tests(\@connect_info);
92         return;
93     }
94
95     my $extra_count = $self->{extra}{count} || 0;
96
97     my $column_accessor_map_tests = 5;
98     my $num_rescans = 5;
99     $num_rescans-- if $self->{vendor} =~ /^(?:sybase|mysql)\z/i;
100     $num_rescans++ if $self->{vendor} eq 'mssql';
101     $num_rescans++ if $self->{vendor} eq 'Firebird';
102
103     plan tests => @connect_info *
104         (182 + $num_rescans * $column_accessor_map_tests + $extra_count + ($self->{data_type_tests}{test_count} || 0));
105
106     foreach my $info_idx (0..$#connect_info) {
107         my $info = $connect_info[$info_idx];
108
109         @{$self}{qw/dsn user password connect_info_opts/} = @$info;
110
111         $self->create();
112
113         my $schema_class = $self->setup_schema($info);
114         $self->test_schema($schema_class);
115
116         rmtree $DUMP_DIR
117             unless $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP} && $info_idx == $#connect_info;
118     }
119 }
120
121 sub run_only_extra_tests {
122     my ($self, $connect_info) = @_;
123
124     plan tests => @$connect_info * (4 + ($self->{extra}{count} || 0) + ($self->{data_type_tests}{test_count} || 0));
125
126     rmtree $DUMP_DIR;
127
128     foreach my $info_idx (0..$#$connect_info) {
129         my $info = $connect_info->[$info_idx];
130
131         @{$self}{qw/dsn user password connect_info_opts/} = @$info;
132
133         $self->drop_extra_tables_only;
134
135         my $dbh = $self->dbconnect(1);
136         $dbh->do($_) for @{ $self->{pre_create} || [] };
137         $dbh->do($_) for @{ $self->{extra}{create} || [] };
138
139         if (not ($self->{vendor} eq 'mssql' && $dbh->{Driver}{Name} eq 'Sybase')) {
140             $dbh->do($_) for @{ $self->{data_type_tests}{ddl} || []};
141         }
142
143         $self->{_created} = 1;
144
145         my $file_count = grep /CREATE (?:TABLE|VIEW)/i, @{ $self->{extra}{create} || [] };
146         $file_count++; # schema
147         
148         if (not ($self->{vendor} eq 'mssql' && $dbh->{Driver}{Name} eq 'Sybase')) {
149             $file_count++ for @{ $self->{data_type_tests}{table_names} || [] };
150         }
151
152         my $schema_class = $self->setup_schema($info, $file_count);
153         my ($monikers, $classes) = $self->monikers_and_classes($schema_class);
154         my $conn = $schema_class->clone;
155
156         $self->test_data_types($conn);
157         $self->{extra}{run}->($conn, $monikers, $classes, $self) if $self->{extra}{run};
158
159         if (not ($ENV{SCHEMA_LOADER_TESTS_NOCLEANUP} && $info_idx == $#$connect_info)) {
160             $self->drop_extra_tables_only;
161             rmtree $DUMP_DIR;
162         }
163     }
164 }
165
166 sub drop_extra_tables_only {
167     my $self = shift;
168
169     my $dbh = $self->dbconnect(0);
170
171     $dbh->do($_) for @{ $self->{extra}{pre_drop_ddl} || [] };
172     $dbh->do("DROP TABLE $_") for @{ $self->{extra}{drop} || [] };
173
174     if (not ($self->{vendor} eq 'mssql' && $dbh->{Driver}{Name} eq 'Sybase')) {
175         foreach my $data_type_table (@{ $self->{data_type_tests}{table_names} || [] }) {
176             $dbh->do("DROP TABLE $data_type_table");
177         }
178     }
179 }
180
181 # defined in sub create
182 my (@statements, @statements_reltests, @statements_advanced,
183     @statements_advanced_sqlite, @statements_inline_rels,
184     @statements_implicit_rels);
185
186 sub setup_schema {
187     my ($self, $connect_info, $expected_count) = @_;
188
189     my $schema_class = 'DBIXCSL_Test::Schema';
190
191     my $debug = ($self->{verbose} > 1) ? 1 : 0;
192
193     if (
194       $ENV{SCHEMA_LOADER_TESTS_USE_MOOSE}
195         &&
196       ! DBIx::Class::Schema::Loader::Optional::Dependencies->req_ok_for('use_moose')
197     ) {
198       die sprintf ("Missing dependencies for SCHEMA_LOADER_TESTS_USE_MOOSE: %s\n",
199         DBIx::Class::Schema::Loader::Optional::Dependencies->req_missing_for('use_moose')
200       );
201     }
202
203     my %loader_opts = (
204         constraint              =>
205           qr/^(?:\S+\.)?(?:(?:$self->{vendor}|extra)_?)?loader_?test[0-9]+(?!.*_)/i,
206         relationships           => 1,
207         additional_classes      => 'TestAdditional',
208         additional_base_classes => 'TestAdditionalBase',
209         left_base_classes       => [ qw/TestLeftBase/ ],
210         components              => [ qw/TestComponent/ ],
211         inflect_plural          => { loader_test4 => 'loader_test4zes' },
212         inflect_singular        => { fkid => 'fkid_singular' },
213         moniker_map             => \&_monikerize,
214         custom_column_info      => \&_custom_column_info,
215         debug                   => $debug,
216         use_namespaces          => 0,
217         dump_directory          => $DUMP_DIR,
218         datetime_timezone       => 'Europe/Berlin',
219         datetime_locale         => 'de_DE',
220         use_moose               => $ENV{SCHEMA_LOADER_TESTS_USE_MOOSE},
221         col_collision_map       => { '^(can)\z' => 'caught_collision_%s' },
222         column_accessor_map     => \&test_column_accessor_map,
223         %{ $self->{loader_options} || {} },
224     );
225
226     $loader_opts{db_schema} = $self->{db_schema} if $self->{db_schema};
227
228     Class::Unload->unload($schema_class);
229
230     my $file_count;
231     {
232         my @loader_warnings;
233         local $SIG{__WARN__} = sub { push(@loader_warnings, @_); };
234          eval qq{
235              package $schema_class;
236              use base qw/DBIx::Class::Schema::Loader/;
237      
238              __PACKAGE__->loader_options(\%loader_opts);
239              __PACKAGE__->connection(\@\$connect_info);
240          };
241  
242         ok(!$@, "Loader initialization") or diag $@;
243
244         find sub { return if -d; $file_count++ }, $DUMP_DIR;
245
246         my $standard_sources = not defined $expected_count;
247
248         if ($standard_sources) {
249             $expected_count = 36;
250
251             if (not ($self->{vendor} eq 'mssql' && $connect_info->[0] =~ /Sybase/)) {
252                 $expected_count++ for @{ $self->{data_type_tests}{table_names} || [] };
253             }
254
255             $expected_count += grep /CREATE (?:TABLE|VIEW)/i,
256                 @{ $self->{extra}{create} || [] };
257      
258             $expected_count -= grep /CREATE TABLE/, @statements_inline_rels
259                 if $self->{skip_rels} || $self->{no_inline_rels};
260      
261             $expected_count -= grep /CREATE TABLE/, @statements_implicit_rels
262                 if $self->{skip_rels} || $self->{no_implicit_rels};
263      
264             $expected_count -= grep /CREATE TABLE/, ($self->{vendor} =~ /sqlite/ ? @statements_advanced_sqlite : @statements_advanced), @statements_reltests
265                 if $self->{skip_rels};
266         }
267  
268         is $file_count, $expected_count, 'correct number of files generated';
269  
270         my $warn_count = 2;
271  
272         $warn_count++ for grep /^Bad table or view/, @loader_warnings;
273  
274         $warn_count++ for grep /renaming \S+ relation/, @loader_warnings;
275  
276         $warn_count++ for grep /\b(?!loader_test9)\w+ has no primary key/i, @loader_warnings;
277
278         $warn_count++ for grep /^Column \w+ in table \w+ collides with an inherited method\./, @loader_warnings;
279
280         $warn_count++ for grep { my $w = $_; grep $w =~ $_, @{ $self->{warnings} || [] } } @loader_warnings;
281
282         $warn_count-- for grep { my $w = $_; grep $w =~ $_, @{ $self->{failtrigger_warnings} || [] } } @loader_warnings;
283
284         if ($standard_sources) {
285             if($self->{skip_rels}) {
286                 SKIP: {
287                     is(scalar(@loader_warnings), $warn_count, "No loader warnings")
288                         or diag @loader_warnings;
289                     skip "No missing PK warnings without rels", 1;
290                 }
291             }
292             else {
293                 $warn_count++;
294                 is(scalar(@loader_warnings), $warn_count, "Expected loader warning")
295                     or diag @loader_warnings;
296                 is(grep(/loader_test9 has no primary key/i, @loader_warnings), 1,
297                      "Missing PK warning");
298             }
299         }
300         else {
301             SKIP: {
302                 is scalar(@loader_warnings), $warn_count, 'Correct number of warnings'
303                     or diag @loader_warnings;
304                 skip "not testing standard sources", 1;
305             }
306         }
307     }
308
309     exit if ($file_count||0) != $expected_count;
310    
311     return $schema_class;
312 }
313
314 sub test_schema {
315     my $self = shift;
316     my $schema_class = shift;
317
318     my $conn = $schema_class->clone;
319
320     ($self->{before_tests_run} || sub {})->($conn);
321
322     my ($monikers, $classes) = $self->monikers_and_classes($schema_class);
323
324     my $moniker1 = $monikers->{loader_test1s};
325     my $class1   = $classes->{loader_test1s};
326     my $rsobj1   = $conn->resultset($moniker1);
327     check_no_duplicate_unique_constraints($class1);
328
329     my $moniker2 = $monikers->{loader_test2};
330     my $class2   = $classes->{loader_test2};
331     my $rsobj2   = $conn->resultset($moniker2);
332     check_no_duplicate_unique_constraints($class2);
333
334     my $moniker23 = $monikers->{LOADER_test23} || $monikers->{loader_test23};
335     my $class23   = $classes->{LOADER_test23}  || $classes->{loader_test23};
336     my $rsobj23   = $conn->resultset($moniker1);
337
338     my $moniker24 = $monikers->{LoAdEr_test24} || $monikers->{loader_test24};
339     my $class24   = $classes->{LoAdEr_test24}  || $classes->{loader_test24};
340     my $rsobj24   = $conn->resultset($moniker2);
341
342     my $moniker35 = $monikers->{loader_test35};
343     my $class35   = $classes->{loader_test35};
344     my $rsobj35   = $conn->resultset($moniker35);
345
346     isa_ok( $rsobj1, "DBIx::Class::ResultSet" );
347     isa_ok( $rsobj2, "DBIx::Class::ResultSet" );
348     isa_ok( $rsobj23, "DBIx::Class::ResultSet" );
349     isa_ok( $rsobj24, "DBIx::Class::ResultSet" );
350     isa_ok( $rsobj35, "DBIx::Class::ResultSet" );
351
352     my @columns_lt2 = $class2->columns;
353     is_deeply( \@columns_lt2, [ qw/id dat dat2 set_primary_key can dbix_class_testcomponent meta/ ], "Column Ordering" );
354
355     is $class2->column_info('can')->{accessor}, 'caught_collision_can',
356         'accessor for column name that conflicts with a UNIVERSAL method renamed based on col_collision_map';
357
358     is $class2->column_info('set_primary_key')->{accessor}, undef,
359         'accessor for column name that conflicts with a result base class method removed';
360
361     is $class2->column_info('dbix_class_testcomponent')->{accessor}, undef,
362         'accessor for column name that conflicts with a component class method removed';
363
364     is $class2->column_info('meta')->{accessor}, undef,
365         'accessor for column name that conflicts with Moose removed';
366
367     my %uniq1 = $class1->unique_constraints;
368     my $uniq1_test = 0;
369     foreach my $ucname (keys %uniq1) {
370         my $cols_arrayref = $uniq1{$ucname};
371         if(@$cols_arrayref == 1 && $cols_arrayref->[0] eq 'dat') {
372            $uniq1_test = 1;
373            last;
374         }
375     }
376     ok($uniq1_test, "Unique constraint");
377
378     is($moniker1, 'LoaderTest1', 'moniker singularisation');
379
380     my %uniq2 = $class2->unique_constraints;
381     my $uniq2_test = 0;
382     foreach my $ucname (keys %uniq2) {
383         my $cols_arrayref = $uniq2{$ucname};
384         if(@$cols_arrayref == 2
385            && $cols_arrayref->[0] eq 'dat2'
386            && $cols_arrayref->[1] eq 'dat') {
387             $uniq2_test = 2;
388             last;
389         }
390     }
391     ok($uniq2_test, "Multi-col unique constraint");
392
393     is($moniker2, 'LoaderTest2X', "moniker_map testing");
394
395     SKIP: {
396         can_ok( $class1, 'test_additional_base' )
397             or skip "Pre-requisite test failed", 1;
398         is( $class1->test_additional_base, "test_additional_base",
399             "Additional Base method" );
400     }
401
402     SKIP: {
403         can_ok( $class1, 'test_additional_base_override' )
404             or skip "Pre-requisite test failed", 1;
405         is( $class1->test_additional_base_override,
406             "test_left_base_override",
407             "Left Base overrides Additional Base method" );
408     }
409
410     SKIP: {
411         can_ok( $class1, 'test_additional_base_additional' )
412             or skip "Pre-requisite test failed", 1;
413         is( $class1->test_additional_base_additional, "test_additional",
414             "Additional Base can use Additional package method" );
415     }
416
417     SKIP: {
418         can_ok( $class1, 'dbix_class_testcomponent' )
419             or skip "Pre-requisite test failed", 1;
420         is( $class1->dbix_class_testcomponent,
421             'dbix_class_testcomponent works',
422             'Additional Component' );
423     }
424
425     SKIP: {
426         can_ok( $class1, 'loader_test1_classmeth' )
427             or skip "Pre-requisite test failed", 1;
428         is( $class1->loader_test1_classmeth, 'all is well', 'Class method' );
429     }
430
431     ok( $class1->column_info('id')->{is_auto_increment}, 'is_auto_increment detection' );
432
433     my $obj    = $rsobj1->find(1);
434     is( $obj->id,  1, "Find got the right row" );
435     is( $obj->dat, "foo", "Column value" );
436     is( $rsobj2->count, 4, "Count" );
437     my $saved_id;
438     eval {
439         my $new_obj1 = $rsobj1->create({ dat => 'newthing' });
440         $saved_id = $new_obj1->id;
441     };
442     ok(!$@, "Inserting new record using a PK::Auto key didn't die") or diag $@;
443     ok($saved_id, "Got PK::Auto-generated id");
444
445     my $new_obj1 = $rsobj1->search({ dat => 'newthing' })->first;
446     ok($new_obj1, "Found newly inserted PK::Auto record");
447     is($new_obj1->id, $saved_id, "Correct PK::Auto-generated id");
448
449     my ($obj2) = $rsobj2->search({ dat => 'bbb' })->first;
450     is( $obj2->id, 2 );
451
452     is(
453         $class35->column_info('a_varchar')->{default_value}, 'foo',
454         'constant character default',
455     );
456
457     is(
458         $class35->column_info('an_int')->{default_value}, 42,
459         'constant integer default',
460     );
461
462     is(
463         $class35->column_info('a_negative_int')->{default_value}, -42,
464         'constant negative integer default',
465     );
466
467     cmp_ok(
468         $class35->column_info('a_double')->{default_value}, '==', 10.555,
469         'constant numeric default',
470     );
471
472     cmp_ok(
473         $class35->column_info('a_negative_double')->{default_value}, '==', -10.555,
474         'constant negative numeric default',
475     );
476
477     my $function_default = $class35->column_info('a_function')->{default_value};
478
479     isa_ok( $function_default, 'SCALAR', 'default_value for function default' );
480     is_deeply(
481         $function_default, \$self->{default_function},
482         'default_value for function default is correct'
483     );
484
485     SKIP: {
486         skip $self->{skip_rels}, 116 if $self->{skip_rels};
487
488         my $moniker3 = $monikers->{loader_test3};
489         my $class3   = $classes->{loader_test3};
490         my $rsobj3   = $conn->resultset($moniker3);
491
492         my $moniker4 = $monikers->{loader_test4};
493         my $class4   = $classes->{loader_test4};
494         my $rsobj4   = $conn->resultset($moniker4);
495
496         my $moniker5 = $monikers->{loader_test5};
497         my $class5   = $classes->{loader_test5};
498         my $rsobj5   = $conn->resultset($moniker5);
499
500         my $moniker6 = $monikers->{loader_test6};
501         my $class6   = $classes->{loader_test6};
502         my $rsobj6   = $conn->resultset($moniker6);
503
504         my $moniker7 = $monikers->{loader_test7};
505         my $class7   = $classes->{loader_test7};
506         my $rsobj7   = $conn->resultset($moniker7);
507
508         my $moniker8 = $monikers->{loader_test8};
509         my $class8   = $classes->{loader_test8};
510         my $rsobj8   = $conn->resultset($moniker8);
511
512         my $moniker9 = $monikers->{loader_test9};
513         my $class9   = $classes->{loader_test9};
514         my $rsobj9   = $conn->resultset($moniker9);
515
516         my $moniker16 = $monikers->{loader_test16};
517         my $class16   = $classes->{loader_test16};
518         my $rsobj16   = $conn->resultset($moniker16);
519
520         my $moniker17 = $monikers->{loader_test17};
521         my $class17   = $classes->{loader_test17};
522         my $rsobj17   = $conn->resultset($moniker17);
523
524         my $moniker18 = $monikers->{loader_test18};
525         my $class18   = $classes->{loader_test18};
526         my $rsobj18   = $conn->resultset($moniker18);
527
528         my $moniker19 = $monikers->{loader_test19};
529         my $class19   = $classes->{loader_test19};
530         my $rsobj19   = $conn->resultset($moniker19);
531
532         my $moniker20 = $monikers->{loader_test20};
533         my $class20   = $classes->{loader_test20};
534         my $rsobj20   = $conn->resultset($moniker20);
535
536         my $moniker21 = $monikers->{loader_test21};
537         my $class21   = $classes->{loader_test21};
538         my $rsobj21   = $conn->resultset($moniker21);
539
540         my $moniker22 = $monikers->{loader_test22};
541         my $class22   = $classes->{loader_test22};
542         my $rsobj22   = $conn->resultset($moniker22);
543
544         my $moniker25 = $monikers->{loader_test25};
545         my $class25   = $classes->{loader_test25};
546         my $rsobj25   = $conn->resultset($moniker25);
547
548         my $moniker26 = $monikers->{loader_test26};
549         my $class26   = $classes->{loader_test26};
550         my $rsobj26   = $conn->resultset($moniker26);
551
552         my $moniker27 = $monikers->{loader_test27};
553         my $class27   = $classes->{loader_test27};
554         my $rsobj27   = $conn->resultset($moniker27);
555
556         my $moniker28 = $monikers->{loader_test28};
557         my $class28   = $classes->{loader_test28};
558         my $rsobj28   = $conn->resultset($moniker28);
559
560         my $moniker29 = $monikers->{loader_test29};
561         my $class29   = $classes->{loader_test29};
562         my $rsobj29   = $conn->resultset($moniker29);
563
564         my $moniker31 = $monikers->{loader_test31};
565         my $class31   = $classes->{loader_test31};
566         my $rsobj31   = $conn->resultset($moniker31);
567
568         my $moniker32 = $monikers->{loader_test32};
569         my $class32   = $classes->{loader_test32};
570         my $rsobj32   = $conn->resultset($moniker32);
571
572         my $moniker33 = $monikers->{loader_test33};
573         my $class33   = $classes->{loader_test33};
574         my $rsobj33   = $conn->resultset($moniker33);
575
576         my $moniker34 = $monikers->{loader_test34};
577         my $class34   = $classes->{loader_test34};
578         my $rsobj34   = $conn->resultset($moniker34);
579
580         my $moniker36 = $monikers->{loader_test36};
581         my $class36   = $classes->{loader_test36};
582         my $rsobj36   = $conn->resultset($moniker36);
583         
584         isa_ok( $rsobj3, "DBIx::Class::ResultSet" );
585         isa_ok( $rsobj4, "DBIx::Class::ResultSet" );
586         isa_ok( $rsobj5, "DBIx::Class::ResultSet" );
587         isa_ok( $rsobj6, "DBIx::Class::ResultSet" );
588         isa_ok( $rsobj7, "DBIx::Class::ResultSet" );
589         isa_ok( $rsobj8, "DBIx::Class::ResultSet" );
590         isa_ok( $rsobj9, "DBIx::Class::ResultSet" );
591         isa_ok( $rsobj16, "DBIx::Class::ResultSet" );
592         isa_ok( $rsobj17, "DBIx::Class::ResultSet" );
593         isa_ok( $rsobj18, "DBIx::Class::ResultSet" );
594         isa_ok( $rsobj19, "DBIx::Class::ResultSet" );
595         isa_ok( $rsobj20, "DBIx::Class::ResultSet" );
596         isa_ok( $rsobj21, "DBIx::Class::ResultSet" );
597         isa_ok( $rsobj22, "DBIx::Class::ResultSet" );
598         isa_ok( $rsobj25, "DBIx::Class::ResultSet" );
599         isa_ok( $rsobj26, "DBIx::Class::ResultSet" );
600         isa_ok( $rsobj27, "DBIx::Class::ResultSet" );
601         isa_ok( $rsobj28, "DBIx::Class::ResultSet" );
602         isa_ok( $rsobj29, "DBIx::Class::ResultSet" );
603         isa_ok( $rsobj31, "DBIx::Class::ResultSet" );
604         isa_ok( $rsobj32, "DBIx::Class::ResultSet" );
605         isa_ok( $rsobj33, "DBIx::Class::ResultSet" );
606         isa_ok( $rsobj34, "DBIx::Class::ResultSet" );
607         isa_ok( $rsobj36, "DBIx::Class::ResultSet" );
608
609         # basic rel test
610         my $obj4 = $rsobj4->find(123);
611         isa_ok( $obj4->fkid_singular, $class3);
612
613         ok($class4->column_info('fkid')->{is_foreign_key}, 'Foreign key detected');
614
615         my $obj3 = $rsobj3->find(1);
616         my $rs_rel4 = $obj3->search_related('loader_test4zes');
617         isa_ok( $rs_rel4->first, $class4);
618
619         is( $class4->column_info('crumb_crisp_coating')->{accessor},  'trivet',
620             'column_accessor_map is being run' );
621
622         # check rel naming with prepositions
623         ok ($rsobj4->result_source->has_relationship('loader_test5s_to'),
624             "rel with preposition 'to' pluralized correctly");
625
626         ok ($rsobj4->result_source->has_relationship('loader_test5s_from'),
627             "rel with preposition 'from' pluralized correctly");
628
629         # check default relationship attributes
630         is $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{cascade_delete}, 0,
631             'cascade_delete => 0 on has_many by default';
632
633         is $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{cascade_copy}, 0,
634             'cascade_copy => 0 on has_many by default';
635
636         ok ((not exists $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{on_delete}),
637             'has_many does not have on_delete');
638
639         ok ((not exists $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{on_update}),
640             'has_many does not have on_update');
641
642         ok ((not exists $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{is_deferrable}),
643             'has_many does not have is_deferrable');
644
645         is $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_delete}, 'CASCADE',
646             "on_delete => 'CASCADE' on belongs_to by default";
647
648         is $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_update}, 'CASCADE',
649             "on_update => 'CASCADE' on belongs_to by default";
650
651         is $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{is_deferrable}, 1,
652             "is_deferrable => 1 on belongs_to by default";
653
654         ok ((not exists $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{cascade_delete}),
655             'belongs_to does not have cascade_delete');
656
657         ok ((not exists $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{cascade_copy}),
658             'belongs_to does not have cascade_copy');
659
660         is $rsobj27->result_source->relationship_info('loader_test28')->{attrs}{cascade_delete}, 0,
661             'cascade_delete => 0 on might_have by default';
662
663         is $rsobj27->result_source->relationship_info('loader_test28')->{attrs}{cascade_copy}, 0,
664             'cascade_copy => 0 on might_have by default';
665
666         ok ((not exists $rsobj27->result_source->relationship_info('loader_test28')->{attrs}{on_delete}),
667             'might_have does not have on_delete');
668
669         ok ((not exists $rsobj27->result_source->relationship_info('loader_test28')->{attrs}{on_update}),
670             'might_have does not have on_update');
671
672         ok ((not exists $rsobj27->result_source->relationship_info('loader_test28')->{attrs}{is_deferrable}),
673             'might_have does not have is_deferrable');
674
675         # find on multi-col pk
676         if ($conn->_loader->preserve_case) {
677             my $obj5 = $rsobj5->find({id1 => 1, iD2 => 1});
678             is $obj5->i_d2, 1, 'Find on multi-col PK';
679         }
680         else {
681             my $obj5 = $rsobj5->find({id1 => 1, id2 => 1});
682             is $obj5->id2, 1, 'Find on multi-col PK';
683         }
684
685         # mulit-col fk def
686         my $obj6 = $rsobj6->find(1);
687         isa_ok( $obj6->loader_test2, $class2);
688         isa_ok( $obj6->loader_test5, $class5);
689
690         ok($class6->column_info('loader_test2_id')->{is_foreign_key}, 'Foreign key detected');
691         ok($class6->column_info('id')->{is_foreign_key}, 'Foreign key detected');
692
693         my $id2_info = eval { $class6->column_info('id2') } ||
694                         $class6->column_info('Id2');
695         ok($id2_info->{is_foreign_key}, 'Foreign key detected');
696
697         # fk that references a non-pk key (UNIQUE)
698         my $obj8 = $rsobj8->find(1);
699         isa_ok( $obj8->loader_test7, $class7);
700
701         ok($class8->column_info('loader_test7')->{is_foreign_key}, 'Foreign key detected');
702
703         # test double-fk 17 ->-> 16
704         my $obj17 = $rsobj17->find(33);
705
706         my $rs_rel16_one = $obj17->loader16_one;
707         isa_ok($rs_rel16_one, $class16);
708         is($rs_rel16_one->dat, 'y16', "Multiple FKs to same table");
709
710         ok($class17->column_info('loader16_one')->{is_foreign_key}, 'Foreign key detected');
711
712         my $rs_rel16_two = $obj17->loader16_two;
713         isa_ok($rs_rel16_two, $class16);
714         is($rs_rel16_two->dat, 'z16', "Multiple FKs to same table");
715
716         ok($class17->column_info('loader16_two')->{is_foreign_key}, 'Foreign key detected');
717
718         my $obj16 = $rsobj16->find(2);
719         my $rs_rel17 = $obj16->search_related('loader_test17_loader16_ones');
720         isa_ok($rs_rel17->first, $class17);
721         is($rs_rel17->first->id, 3, "search_related with multiple FKs from same table");
722         
723         # XXX test m:m 18 <- 20 -> 19
724         ok($class20->column_info('parent')->{is_foreign_key}, 'Foreign key detected');
725         ok($class20->column_info('child')->{is_foreign_key}, 'Foreign key detected');
726         
727         # XXX test double-fk m:m 21 <- 22 -> 21
728         ok($class22->column_info('parent')->{is_foreign_key}, 'Foreign key detected');
729         ok($class22->column_info('child')->{is_foreign_key}, 'Foreign key detected');
730
731         # test double multi-col fk 26 -> 25
732         my $obj26 = $rsobj26->find(33);
733
734         my $rs_rel25_one = $obj26->loader_test25_id_rel1;
735         isa_ok($rs_rel25_one, $class25);
736         is($rs_rel25_one->dat, 'x25', "Multiple multi-col FKs to same table");
737
738         ok($class26->column_info('id')->{is_foreign_key}, 'Foreign key detected');
739         ok($class26->column_info('rel1')->{is_foreign_key}, 'Foreign key detected');
740         ok($class26->column_info('rel2')->{is_foreign_key}, 'Foreign key detected');
741
742         my $rs_rel25_two = $obj26->loader_test25_id_rel2;
743         isa_ok($rs_rel25_two, $class25);
744         is($rs_rel25_two->dat, 'y25', "Multiple multi-col FKs to same table");
745
746         my $obj25 = $rsobj25->find(3,42);
747         my $rs_rel26 = $obj25->search_related('loader_test26_id_rel1s');
748         isa_ok($rs_rel26->first, $class26);
749         is($rs_rel26->first->id, 3, "search_related with multiple multi-col FKs from same table");
750
751         # test one-to-one rels
752         my $obj27 = $rsobj27->find(1);
753         my $obj28 = $obj27->loader_test28;
754         isa_ok($obj28, $class28);
755         is($obj28->get_column('id'), 1, "One-to-one relationship with PRIMARY FK");
756
757         ok($class28->column_info('id')->{is_foreign_key}, 'Foreign key detected');
758
759         my $obj29 = $obj27->loader_test29;
760         isa_ok($obj29, $class29);
761         is($obj29->id, 1, "One-to-one relationship with UNIQUE FK");
762
763         ok($class29->column_info('fk')->{is_foreign_key}, 'Foreign key detected');
764
765         $obj27 = $rsobj27->find(2);
766         is($obj27->loader_test28, undef, "Undef for missing one-to-one row");
767         is($obj27->loader_test29, undef, "Undef for missing one-to-one row");
768
769         # test outer join for nullable referring columns:
770         is $class32->column_info('rel2')->{is_nullable}, 1,
771           'is_nullable detection';
772
773         ok($class32->column_info('rel1')->{is_foreign_key}, 'Foreign key detected');
774         ok($class32->column_info('rel2')->{is_foreign_key}, 'Foreign key detected');
775         
776         my $obj32 = $rsobj32->find(1,{prefetch=>[qw/rel1 rel2/]});
777         my $obj34 = $rsobj34->find(
778           1,{prefetch=>[qw/loader_test33_id_rel1 loader_test33_id_rel2/]}
779         );
780         isa_ok($obj32,$class32);
781         isa_ok($obj34,$class34);
782
783         ok($class34->column_info('id')->{is_foreign_key}, 'Foreign key detected');
784         ok($class34->column_info('rel1')->{is_foreign_key}, 'Foreign key detected');
785         ok($class34->column_info('rel2')->{is_foreign_key}, 'Foreign key detected');
786
787         my $rs_rel31_one = $obj32->rel1;
788         my $rs_rel31_two = $obj32->rel2;
789         isa_ok($rs_rel31_one, $class31);
790         is($rs_rel31_two, undef);
791
792         my $rs_rel33_one = $obj34->loader_test33_id_rel1;
793         my $rs_rel33_two = $obj34->loader_test33_id_rel2;
794
795         isa_ok($rs_rel33_one,$class33);
796         is($rs_rel33_two, undef);
797
798         # from Chisel's tests...
799         my $moniker10 = $monikers->{loader_test10};
800         my $class10   = $classes->{loader_test10};
801         my $rsobj10   = $conn->resultset($moniker10);
802
803         my $moniker11 = $monikers->{loader_test11};
804         my $class11   = $classes->{loader_test11};
805         my $rsobj11   = $conn->resultset($moniker11);
806
807         isa_ok( $rsobj10, "DBIx::Class::ResultSet" );
808         isa_ok( $rsobj11, "DBIx::Class::ResultSet" );
809
810         ok($class10->column_info('loader_test11')->{is_foreign_key}, 'Foreign key detected');
811         ok($class11->column_info('loader_test10')->{is_foreign_key}, 'Foreign key detected');
812
813         my $obj10 = $rsobj10->create({ subject => 'xyzzy' });
814
815         $obj10->update();
816         ok( defined $obj10, 'Create row' );
817
818         my $obj11 = $rsobj11->create({ loader_test10 => $obj10->id() });
819         $obj11->update();
820         ok( defined $obj11, 'Create related row' );
821
822         eval {
823             my $obj10_2 = $obj11->loader_test10;
824             $obj10_2->update({ loader_test11 => $obj11->id11 });
825         };
826         diag $@ if $@;
827         ok(!$@, "Setting up circular relationship");
828
829         SKIP: {
830             skip 'Previous eval block failed', 3 if $@;
831     
832             my $results = $rsobj10->search({ subject => 'xyzzy' });
833             is( $results->count(), 1, 'No duplicate row created' );
834
835             my $obj10_3 = $results->first();
836             isa_ok( $obj10_3, $class10 );
837             is( $obj10_3->loader_test11()->id(), $obj11->id(),
838                 'Circular rel leads back to same row' );
839         }
840
841         SKIP: {
842             skip 'This vendor cannot do inline relationship definitions', 11
843                 if $self->{no_inline_rels};
844
845             my $moniker12 = $monikers->{loader_test12};
846             my $class12   = $classes->{loader_test12};
847             my $rsobj12   = $conn->resultset($moniker12);
848
849             my $moniker13 = $monikers->{loader_test13};
850             my $class13   = $classes->{loader_test13};
851             my $rsobj13   = $conn->resultset($moniker13);
852
853             isa_ok( $rsobj12, "DBIx::Class::ResultSet" ); 
854             isa_ok( $rsobj13, "DBIx::Class::ResultSet" );
855
856             ok($class13->column_info('id')->{is_foreign_key}, 'Foreign key detected');
857             ok($class13->column_info('loader_test12')->{is_foreign_key}, 'Foreign key detected');
858             ok($class13->column_info('dat')->{is_foreign_key}, 'Foreign key detected');
859
860             my $obj13 = $rsobj13->find(1);
861             isa_ok( $obj13->id, $class12 );
862             isa_ok( $obj13->loader_test12, $class12);
863             isa_ok( $obj13->dat, $class12);
864
865             my $obj12 = $rsobj12->find(1);
866             isa_ok( $obj12->loader_test13, $class13 );
867
868             # relname is preserved when another fk is added
869
870             skip 'Sybase cannot add FKs via ALTER TABLE', 2
871                 if $self->{vendor} eq 'sybase';
872
873             {
874                 local $SIG{__WARN__} = sub { warn @_ unless $_[0] =~ /invalidates \d+ active statement/ };
875                 $conn->storage->disconnect; # for mssql
876             }
877
878             isa_ok $rsobj3->find(1)->loader_test4zes, 'DBIx::Class::ResultSet';
879
880             $conn->storage->dbh->do('ALTER TABLE loader_test4 ADD fkid2 INTEGER REFERENCES loader_test3 (id)');
881
882             $conn->storage->disconnect; # for firebird
883
884             $self->rescan_without_warnings($conn);
885
886             isa_ok eval { $rsobj3->find(1)->loader_test4zes }, 'DBIx::Class::ResultSet',
887                 'relationship name preserved when another foreign key is added in remote table';
888         }
889
890         SKIP: {
891             skip 'This vendor cannot do out-of-line implicit rel defs', 4
892                 if $self->{no_implicit_rels};
893             my $moniker14 = $monikers->{loader_test14};
894             my $class14   = $classes->{loader_test14};
895             my $rsobj14   = $conn->resultset($moniker14);
896
897             my $moniker15 = $monikers->{loader_test15};
898             my $class15   = $classes->{loader_test15};
899             my $rsobj15   = $conn->resultset($moniker15);
900
901             isa_ok( $rsobj14, "DBIx::Class::ResultSet" ); 
902             isa_ok( $rsobj15, "DBIx::Class::ResultSet" );
903
904             ok($class15->column_info('loader_test14')->{is_foreign_key}, 'Foreign key detected');
905
906             my $obj15 = $rsobj15->find(1);
907             isa_ok( $obj15->loader_test14, $class14 );
908         }
909     }
910
911     # test custom_column_info and datetime_timezone/datetime_locale
912     {
913         my $class35 = $classes->{loader_test35};
914         my $class36 = $classes->{loader_test36};
915
916         ok($class35->column_info('an_int')->{is_numeric}, 'custom_column_info');
917
918         is($class36->column_info('a_date')->{locale},'de_DE','datetime_locale');
919         is($class36->column_info('a_date')->{timezone},'Europe/Berlin','datetime_timezone');
920
921         ok($class36->column_info('b_char_as_data')->{inflate_datetime},'custom_column_info');
922         is($class36->column_info('b_char_as_data')->{locale},'de_DE','datetime_locale');
923         is($class36->column_info('b_char_as_data')->{timezone},'Europe/Berlin','datetime_timezone');
924
925         ok($class36->column_info('c_char_as_data')->{inflate_date},'custom_column_info');
926         is($class36->column_info('c_char_as_data')->{locale},'de_DE','datetime_locale');
927         is($class36->column_info('c_char_as_data')->{timezone},'Europe/Berlin','datetime_timezone');
928     }
929
930     # rescan and norewrite test
931     {
932         my @statements_rescan = (
933             qq{
934                 CREATE TABLE loader_test30 (
935                     id INTEGER NOT NULL PRIMARY KEY,
936                     loader_test2 INTEGER NOT NULL,
937                     FOREIGN KEY (loader_test2) REFERENCES loader_test2 (id)
938                 ) $self->{innodb}
939             },
940             q{ INSERT INTO loader_test30 (id,loader_test2) VALUES(123, 1) },
941             q{ INSERT INTO loader_test30 (id,loader_test2) VALUES(321, 2) },
942         );
943
944         # get md5
945         my $digest  = Digest::MD5->new;
946
947         my $find_cb = sub {
948             return if -d;
949             return if /^(?:LoaderTest30|LoaderTest1|LoaderTest2X)\.pm\z/;
950
951             open my $fh, '<', $_ or die "Could not open $_ for reading: $!";
952             binmode $fh;
953             $digest->addfile($fh);
954         };
955
956         find $find_cb, $DUMP_DIR;
957
958 #        system "rm -f /tmp/before_rescan/* /tmp/after_rescan/*";
959 #        system "cp $tdir/common_dump/DBIXCSL_Test/Schema/*.pm /tmp/before_rescan";
960
961         my $before_digest = $digest->b64digest;
962
963         $conn->storage->disconnect; # needed for Firebird and Informix
964         my $dbh = $self->dbconnect(1);
965         $dbh->do($_) for @statements_rescan;
966         $dbh->disconnect;
967
968         sleep 1;
969
970         my @new = $self->rescan_without_warnings($conn);
971
972         is_deeply(\@new, [ qw/LoaderTest30/ ], "Rescan");
973
974 #        system "cp t/_common_dump/DBIXCSL_Test/Schema/*.pm /tmp/after_rescan";
975
976         $digest = Digest::MD5->new;
977         find $find_cb, $DUMP_DIR;
978         my $after_digest = $digest->b64digest;
979
980         is $before_digest, $after_digest,
981             'dumped files are not rewritten when there is no modification';
982
983         my $rsobj30   = $conn->resultset('LoaderTest30');
984         isa_ok($rsobj30, 'DBIx::Class::ResultSet');
985
986         SKIP: {
987             skip 'no rels', 2 if $self->{skip_rels};
988
989             my $obj30 = $rsobj30->find(123);
990             isa_ok( $obj30->loader_test2, $class2);
991
992             ok($rsobj30->result_source->column_info('loader_test2')->{is_foreign_key},
993                'Foreign key detected');
994         }
995
996         $conn->storage->disconnect; # for Firebird
997         $conn->storage->dbh->do("DROP TABLE loader_test30");
998
999         @new = $self->rescan_without_warnings($conn);
1000
1001         is_deeply(\@new, [], 'no new tables on rescan');
1002
1003         throws_ok { $conn->resultset('LoaderTest30') }
1004             qr/Can't find source/,
1005             'source unregistered for dropped table after rescan';
1006     }
1007
1008     $self->test_data_types($conn);
1009
1010     # run extra tests
1011     $self->{extra}{run}->($conn, $monikers, $classes, $self) if $self->{extra}{run};
1012
1013     $self->test_preserve_case($conn);
1014
1015     $self->drop_tables unless $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP};
1016
1017     $conn->storage->disconnect;
1018 }
1019
1020 sub test_data_types {
1021     my ($self, $conn) = @_;
1022
1023     SKIP: {
1024         if (my $test_count = $self->{data_type_tests}{test_count}) {
1025             if ($self->{vendor} eq 'mssql' && $conn->storage->dbh->{Driver}{Name} eq 'Sybase') {
1026                 skip 'DBD::Sybase does not work with the data_type tests on latest SQL Server', $test_count;
1027             }
1028
1029             my $data_type_tests = $self->{data_type_tests};
1030
1031             foreach my $moniker (@{ $data_type_tests->{table_monikers} }) {
1032                 my $columns = $data_type_tests->{columns}{$moniker};
1033
1034                 my $rsrc = $conn->resultset($moniker)->result_source;
1035
1036                 while (my ($col_name, $expected_info) = each %$columns) {
1037                     my %info = %{ $rsrc->column_info($col_name) };
1038                     delete @info{qw/is_nullable timezone locale sequence/};
1039
1040                     my $text_col_def = dumper_squashed \%info;
1041
1042                     my $text_expected_info = dumper_squashed $expected_info;
1043
1044                     is_deeply \%info, $expected_info,
1045                         "test column $col_name has definition: $text_col_def expecting: $text_expected_info";
1046                 }
1047             }
1048         }
1049     }
1050 }
1051
1052 sub test_preserve_case {
1053     my ($self, $conn) = @_;
1054
1055     my ($oqt, $cqt) = $self->get_oqt_cqt(always => 1); # open quote, close quote
1056
1057     my $dbh = $self->dbconnect;
1058
1059     $dbh->do($_) for (
1060 qq|
1061     CREATE TABLE ${oqt}LoaderTest40${cqt} (
1062         ${oqt}Id${cqt} INTEGER NOT NULL PRIMARY KEY,
1063         ${oqt}Foo3Bar${cqt} VARCHAR(100) NOT NULL
1064     ) $self->{innodb}
1065 |,
1066 qq|
1067     CREATE TABLE ${oqt}LoaderTest41${cqt} (
1068         ${oqt}Id${cqt} INTEGER NOT NULL PRIMARY KEY,
1069         ${oqt}LoaderTest40Id${cqt} INTEGER,
1070         FOREIGN KEY (${oqt}LoaderTest40Id${cqt}) REFERENCES ${oqt}LoaderTest40${cqt} (${oqt}Id${cqt})
1071     ) $self->{innodb}
1072 |,
1073 qq| INSERT INTO ${oqt}LoaderTest40${cqt} VALUES (1, 'foo') |,
1074 qq| INSERT INTO ${oqt}LoaderTest41${cqt} VALUES (1, 1) |,
1075     );
1076     $conn->storage->disconnect;
1077
1078     local $conn->_loader->{preserve_case} = 1;
1079     $conn->_loader->_setup;
1080
1081     $self->rescan_without_warnings($conn);
1082
1083     if (not $self->{skip_rels}) {
1084         is $conn->resultset('LoaderTest41')->find(1)->loader_test40->foo3_bar, 'foo',
1085             'rel and accessor for mixed-case column name in mixed case table';
1086     }
1087     else {
1088         is $conn->resultset('LoaderTest40')->find(1)->foo3_bar, 'foo',
1089             'accessor for mixed-case column name in mixed case table';
1090     }
1091 }
1092
1093 sub monikers_and_classes {
1094     my ($self, $schema_class) = @_;
1095     my ($monikers, $classes);
1096
1097     foreach my $source_name ($schema_class->sources) {
1098         my $table_name = $schema_class->source($source_name)->from;
1099
1100         $table_name = $$table_name if ref $table_name;
1101
1102         $monikers->{$table_name} = $source_name;
1103         $classes->{$table_name} = $schema_class . q{::} . $source_name;
1104
1105         # some DBs (Firebird) uppercase everything
1106         $monikers->{lc $table_name} = $source_name;
1107         $classes->{lc $table_name} = $schema_class . q{::} . $source_name;
1108     }
1109
1110     return ($monikers, $classes);
1111 }
1112
1113 sub check_no_duplicate_unique_constraints {
1114     my ($class) = @_;
1115
1116     # unique_constraints() automatically includes the PK, if any
1117     my %uc_cols;
1118     ++$uc_cols{ join ", ", @$_ }
1119         for values %{ { $class->unique_constraints } };
1120     my $dup_uc = grep { $_ > 1 } values %uc_cols;
1121
1122     is($dup_uc, 0, "duplicate unique constraints ($class)")
1123         or diag "uc_cols: @{[ %uc_cols ]}";
1124 }
1125
1126 sub dbconnect {
1127     my ($self, $complain) = @_;
1128
1129     require DBIx::Class::Storage::DBI;
1130     my $storage = DBIx::Class::Storage::DBI->new;
1131
1132     $complain = defined $complain ? $complain : 1;
1133
1134     $storage->connect_info([
1135         @{ $self }{qw/dsn user password/},
1136         {
1137             unsafe => 1,
1138             RaiseError => $complain,
1139             ShowErrorStatement => $complain,
1140             PrintError => 0,
1141             %{ $self->{connect_info_opts} || {} },
1142         },
1143     ]);
1144
1145     my $dbh = eval { $storage->dbh };
1146     die "Failed to connect to database: $@" if !$dbh;
1147
1148     $self->{storage} = $storage; # storage DESTROY disconnects
1149
1150     return $dbh;
1151 }
1152
1153 sub get_oqt_cqt {
1154     my $self = shift;
1155     my %opts = @_;
1156
1157     if ((not $opts{always}) && $self->{preserve_case_mode_is_exclusive}) {
1158         return ('', '');
1159     }
1160
1161     # XXX should get quote_char from the storage of an initialized loader.
1162     my ($oqt, $cqt); # open quote, close quote
1163     if (ref $self->{quote_char}) {
1164         ($oqt, $cqt) = @{ $self->{quote_char} };
1165     }
1166     else {
1167         $oqt = $cqt = $self->{quote_char} || '';
1168     }
1169
1170     return ($oqt, $cqt);
1171 }
1172
1173 sub create {
1174     my $self = shift;
1175
1176     $self->{_created} = 1;
1177
1178     $self->drop_tables;
1179
1180     my $make_auto_inc = $self->{auto_inc_cb} || sub {};
1181     @statements = (
1182         qq{
1183             CREATE TABLE loader_test1s (
1184                 id $self->{auto_inc_pk},
1185                 dat VARCHAR(32) NOT NULL UNIQUE
1186             ) $self->{innodb}
1187         },
1188         $make_auto_inc->(qw/loader_test1s id/),
1189
1190         q{ INSERT INTO loader_test1s (dat) VALUES('foo') },
1191         q{ INSERT INTO loader_test1s (dat) VALUES('bar') }, 
1192         q{ INSERT INTO loader_test1s (dat) VALUES('baz') }, 
1193
1194         # also test method collision
1195         qq{ 
1196             CREATE TABLE loader_test2 (
1197                 id $self->{auto_inc_pk},
1198                 dat VARCHAR(32) NOT NULL,
1199                 dat2 VARCHAR(32) NOT NULL,
1200                 set_primary_key INTEGER $self->{null},
1201                 can INTEGER $self->{null},
1202                 dbix_class_testcomponent INTEGER $self->{null},
1203                 meta INTEGER $self->{null},
1204                 UNIQUE (dat2, dat)
1205             ) $self->{innodb}
1206         },
1207         $make_auto_inc->(qw/loader_test2 id/),
1208
1209         q{ INSERT INTO loader_test2 (dat, dat2) VALUES('aaa', 'zzz') }, 
1210         q{ INSERT INTO loader_test2 (dat, dat2) VALUES('bbb', 'yyy') }, 
1211         q{ INSERT INTO loader_test2 (dat, dat2) VALUES('ccc', 'xxx') }, 
1212         q{ INSERT INTO loader_test2 (dat, dat2) VALUES('ddd', 'www') }, 
1213
1214         qq{
1215             CREATE TABLE LOADER_test23 (
1216                 ID INTEGER NOT NULL PRIMARY KEY,
1217                 DAT VARCHAR(32) NOT NULL UNIQUE
1218             ) $self->{innodb}
1219         },
1220
1221         qq{
1222             CREATE TABLE LoAdEr_test24 (
1223                 iD INTEGER NOT NULL PRIMARY KEY,
1224                 DaT VARCHAR(32) NOT NULL UNIQUE
1225             ) $self->{innodb}
1226         },
1227
1228         qq{
1229             CREATE TABLE loader_test35 (
1230                 id INTEGER NOT NULL PRIMARY KEY,
1231                 a_varchar VARCHAR(100) DEFAULT 'foo',
1232                 an_int INTEGER DEFAULT 42,
1233                 a_negative_int INTEGER DEFAULT -42,
1234                 a_double DOUBLE PRECISION DEFAULT 10.555,
1235                 a_negative_double DOUBLE PRECISION DEFAULT -10.555,
1236                 a_function $self->{default_function_def}
1237             ) $self->{innodb}
1238         },
1239
1240         qq{
1241             CREATE TABLE loader_test36 (
1242                 id INTEGER NOT NULL PRIMARY KEY,
1243                 a_date $self->{basic_date_datatype},
1244                 b_char_as_data VARCHAR(100),
1245                 c_char_as_data VARCHAR(100)
1246             ) $self->{innodb}
1247         },
1248     );
1249
1250     # some DBs require mixed case identifiers to be quoted
1251     my ($oqt, $cqt) = $self->get_oqt_cqt;
1252
1253     @statements_reltests = (
1254         qq{
1255             CREATE TABLE loader_test3 (
1256                 id INTEGER NOT NULL PRIMARY KEY,
1257                 dat VARCHAR(32)
1258             ) $self->{innodb}
1259         },
1260
1261         q{ INSERT INTO loader_test3 (id,dat) VALUES(1,'aaa') }, 
1262         q{ INSERT INTO loader_test3 (id,dat) VALUES(2,'bbb') }, 
1263         q{ INSERT INTO loader_test3 (id,dat) VALUES(3,'ccc') }, 
1264         q{ INSERT INTO loader_test3 (id,dat) VALUES(4,'ddd') }, 
1265
1266         qq{
1267             CREATE TABLE loader_test4 (
1268                 id INTEGER NOT NULL PRIMARY KEY,
1269                 fkid INTEGER NOT NULL,
1270                 dat VARCHAR(32),
1271                 crumb_crisp_coating VARCHAR(32) $self->{null},
1272                 FOREIGN KEY( fkid ) REFERENCES loader_test3 (id)
1273             ) $self->{innodb}
1274         },
1275
1276         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(123,1,'aaa') },
1277         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(124,2,'bbb') }, 
1278         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(125,3,'ccc') },
1279         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(126,4,'ddd') },
1280
1281         qq|
1282             CREATE TABLE loader_test5 (
1283                 id1 INTEGER NOT NULL,
1284                 ${oqt}iD2${cqt} INTEGER NOT NULL,
1285                 dat VARCHAR(8),
1286                 from_id INTEGER $self->{null},
1287                 to_id INTEGER $self->{null},
1288                 PRIMARY KEY (id1,${oqt}iD2${cqt}),
1289                 FOREIGN KEY (from_id) REFERENCES loader_test4 (id),
1290                 FOREIGN KEY (to_id) REFERENCES loader_test4 (id)
1291             ) $self->{innodb}
1292         |,
1293
1294         qq| INSERT INTO loader_test5 (id1,${oqt}iD2${cqt},dat) VALUES (1,1,'aaa') |,
1295
1296         qq|
1297             CREATE TABLE loader_test6 (
1298                 id INTEGER NOT NULL PRIMARY KEY,
1299                 ${oqt}Id2${cqt} INTEGER,
1300                 loader_test2_id INTEGER,
1301                 dat VARCHAR(8),
1302                 FOREIGN KEY (loader_test2_id)  REFERENCES loader_test2 (id),
1303                 FOREIGN KEY(id,${oqt}Id2${cqt}) REFERENCES loader_test5 (id1,${oqt}iD2${cqt})
1304             ) $self->{innodb}
1305         |,
1306
1307         (qq| INSERT INTO loader_test6 (id, ${oqt}Id2${cqt},loader_test2_id,dat) | .
1308          q{ VALUES (1, 1,1,'aaa') }),
1309
1310         qq{
1311             CREATE TABLE loader_test7 (
1312                 id INTEGER NOT NULL PRIMARY KEY,
1313                 id2 VARCHAR(8) NOT NULL UNIQUE,
1314                 dat VARCHAR(8)
1315             ) $self->{innodb}
1316         },
1317
1318         q{ INSERT INTO loader_test7 (id,id2,dat) VALUES (1,'aaa','bbb') },
1319
1320         qq{
1321             CREATE TABLE loader_test8 (
1322                 id INTEGER NOT NULL PRIMARY KEY,
1323                 loader_test7 VARCHAR(8) NOT NULL,
1324                 dat VARCHAR(8),
1325                 FOREIGN KEY (loader_test7) REFERENCES loader_test7 (id2)
1326             ) $self->{innodb}
1327         },
1328
1329         (q{ INSERT INTO loader_test8 (id,loader_test7,dat) } .
1330          q{ VALUES (1,'aaa','bbb') }),
1331
1332         qq{
1333             CREATE TABLE loader_test9 (
1334                 loader_test9 VARCHAR(8) NOT NULL
1335             ) $self->{innodb}
1336         },
1337
1338         qq{
1339             CREATE TABLE loader_test16 (
1340                 id INTEGER NOT NULL PRIMARY KEY,
1341                 dat  VARCHAR(8)
1342             ) $self->{innodb}
1343         },
1344
1345         qq{ INSERT INTO loader_test16 (id,dat) VALUES (2,'x16') },
1346         qq{ INSERT INTO loader_test16 (id,dat) VALUES (4,'y16') },
1347         qq{ INSERT INTO loader_test16 (id,dat) VALUES (6,'z16') },
1348
1349         qq{
1350             CREATE TABLE loader_test17 (
1351                 id INTEGER NOT NULL PRIMARY KEY,
1352                 loader16_one INTEGER,
1353                 loader16_two INTEGER,
1354                 FOREIGN KEY (loader16_one) REFERENCES loader_test16 (id),
1355                 FOREIGN KEY (loader16_two) REFERENCES loader_test16 (id)
1356             ) $self->{innodb}
1357         },
1358
1359         qq{ INSERT INTO loader_test17 (id, loader16_one, loader16_two) VALUES (3, 2, 4) },
1360         qq{ INSERT INTO loader_test17 (id, loader16_one, loader16_two) VALUES (33, 4, 6) },
1361
1362         qq{
1363             CREATE TABLE loader_test18 (
1364                 id INTEGER NOT NULL PRIMARY KEY,
1365                 dat  VARCHAR(8)
1366             ) $self->{innodb}
1367         },
1368
1369         qq{ INSERT INTO loader_test18 (id,dat) VALUES (1,'x18') },
1370         qq{ INSERT INTO loader_test18 (id,dat) VALUES (2,'y18') },
1371         qq{ INSERT INTO loader_test18 (id,dat) VALUES (3,'z18') },
1372
1373         qq{
1374             CREATE TABLE loader_test19 (
1375                 id INTEGER NOT NULL PRIMARY KEY,
1376                 dat  VARCHAR(8)
1377             ) $self->{innodb}
1378         },
1379
1380         qq{ INSERT INTO loader_test19 (id,dat) VALUES (4,'x19') },
1381         qq{ INSERT INTO loader_test19 (id,dat) VALUES (5,'y19') },
1382         qq{ INSERT INTO loader_test19 (id,dat) VALUES (6,'z19') },
1383
1384         qq{
1385             CREATE TABLE loader_test20 (
1386                 parent INTEGER NOT NULL,
1387                 child INTEGER NOT NULL,
1388                 PRIMARY KEY (parent, child),
1389                 FOREIGN KEY (parent) REFERENCES loader_test18 (id),
1390                 FOREIGN KEY (child) REFERENCES loader_test19 (id)
1391             ) $self->{innodb}
1392         },
1393
1394         q{ INSERT INTO loader_test20 (parent, child) VALUES (1,4) },
1395         q{ INSERT INTO loader_test20 (parent, child) VALUES (2,5) },
1396         q{ INSERT INTO loader_test20 (parent, child) VALUES (3,6) },
1397
1398         qq{
1399             CREATE TABLE loader_test21 (
1400                 id INTEGER NOT NULL PRIMARY KEY,
1401                 dat  VARCHAR(8)
1402             ) $self->{innodb}
1403         },
1404
1405         q{ INSERT INTO loader_test21 (id,dat) VALUES (7,'a21')},
1406         q{ INSERT INTO loader_test21 (id,dat) VALUES (11,'b21')},
1407         q{ INSERT INTO loader_test21 (id,dat) VALUES (13,'c21')},
1408         q{ INSERT INTO loader_test21 (id,dat) VALUES (17,'d21')},
1409
1410         qq{
1411             CREATE TABLE loader_test22 (
1412                 parent INTEGER NOT NULL,
1413                 child INTEGER NOT NULL,
1414                 PRIMARY KEY (parent, child),
1415                 FOREIGN KEY (parent) REFERENCES loader_test21 (id),
1416                 FOREIGN KEY (child) REFERENCES loader_test21 (id)
1417             ) $self->{innodb}
1418         },
1419
1420         q{ INSERT INTO loader_test22 (parent, child) VALUES (7,11)},
1421         q{ INSERT INTO loader_test22 (parent, child) VALUES (11,13)},
1422         q{ INSERT INTO loader_test22 (parent, child) VALUES (13,17)},
1423
1424         qq{
1425             CREATE TABLE loader_test25 (
1426                 id1 INTEGER NOT NULL,
1427                 id2 INTEGER NOT NULL,
1428                 dat VARCHAR(8),
1429                 PRIMARY KEY (id1,id2)
1430             ) $self->{innodb}
1431         },
1432
1433         q{ INSERT INTO loader_test25 (id1,id2,dat) VALUES (33,5,'x25') },
1434         q{ INSERT INTO loader_test25 (id1,id2,dat) VALUES (33,7,'y25') },
1435         q{ INSERT INTO loader_test25 (id1,id2,dat) VALUES (3,42,'z25') },
1436
1437         qq{
1438             CREATE TABLE loader_test26 (
1439                id INTEGER NOT NULL PRIMARY KEY,
1440                rel1 INTEGER NOT NULL,
1441                rel2 INTEGER NOT NULL,
1442                FOREIGN KEY (id, rel1) REFERENCES loader_test25 (id1, id2),
1443                FOREIGN KEY (id, rel2) REFERENCES loader_test25 (id1, id2)
1444             ) $self->{innodb}
1445         },
1446
1447         q{ INSERT INTO loader_test26 (id,rel1,rel2) VALUES (33,5,7) },
1448         q{ INSERT INTO loader_test26 (id,rel1,rel2) VALUES (3,42,42) },
1449
1450         qq{
1451             CREATE TABLE loader_test27 (
1452                 id INTEGER NOT NULL PRIMARY KEY
1453             ) $self->{innodb}
1454         },
1455
1456         q{ INSERT INTO loader_test27 (id) VALUES (1) },
1457         q{ INSERT INTO loader_test27 (id) VALUES (2) },
1458
1459         qq{
1460             CREATE TABLE loader_test28 (
1461                 id INTEGER NOT NULL PRIMARY KEY,
1462                 FOREIGN KEY (id) REFERENCES loader_test27 (id)
1463             ) $self->{innodb}
1464         },
1465
1466         q{ INSERT INTO loader_test28 (id) VALUES (1) },
1467
1468         qq{
1469             CREATE TABLE loader_test29 (
1470                 id INTEGER NOT NULL PRIMARY KEY,
1471                 fk INTEGER NOT NULL UNIQUE,
1472                 FOREIGN KEY (fk) REFERENCES loader_test27 (id)
1473             ) $self->{innodb}
1474         },
1475
1476         q{ INSERT INTO loader_test29 (id,fk) VALUES (1,1) },
1477
1478         qq{
1479           CREATE TABLE loader_test31 (
1480             id INTEGER NOT NULL PRIMARY KEY
1481           ) $self->{innodb}
1482         },
1483         q{ INSERT INTO loader_test31 (id) VALUES (1) },
1484
1485         qq{
1486           CREATE TABLE loader_test32 (
1487             id INTEGER NOT NULL PRIMARY KEY,
1488             rel1 INTEGER NOT NULL,
1489             rel2 INTEGER $self->{null},
1490             FOREIGN KEY (rel1) REFERENCES loader_test31(id),
1491             FOREIGN KEY (rel2) REFERENCES loader_test31(id)
1492           ) $self->{innodb}
1493         },
1494         q{ INSERT INTO loader_test32 (id,rel1) VALUES (1,1) },
1495
1496         qq{
1497           CREATE TABLE loader_test33 (
1498             id1 INTEGER NOT NULL,
1499             id2 INTEGER NOT NULL,
1500             PRIMARY KEY (id1,id2)
1501           ) $self->{innodb}
1502         },
1503         q{ INSERT INTO loader_test33 (id1,id2) VALUES (1,2) },
1504
1505         qq{
1506           CREATE TABLE loader_test34 (
1507             id INTEGER NOT NULL PRIMARY KEY,
1508             rel1 INTEGER NOT NULL,
1509             rel2 INTEGER $self->{null},
1510             FOREIGN KEY (id,rel1) REFERENCES loader_test33(id1,id2),
1511             FOREIGN KEY (id,rel2) REFERENCES loader_test33(id1,id2)
1512           ) $self->{innodb}
1513         },
1514         q{ INSERT INTO loader_test34 (id,rel1) VALUES (1,2) },
1515     );
1516
1517     @statements_advanced = (
1518         qq{
1519             CREATE TABLE loader_test10 (
1520                 id10 $self->{auto_inc_pk},
1521                 subject VARCHAR(8),
1522                 loader_test11 INTEGER $self->{null}
1523             ) $self->{innodb}
1524         },
1525         $make_auto_inc->(qw/loader_test10 id10/),
1526
1527         qq{
1528             CREATE TABLE loader_test11 (
1529                 id11 $self->{auto_inc_pk},
1530                 a_message VARCHAR(8) DEFAULT 'foo',
1531                 loader_test10 INTEGER $self->{null},
1532                 FOREIGN KEY (loader_test10) REFERENCES loader_test10 (id10)
1533             ) $self->{innodb}
1534         },
1535         $make_auto_inc->(qw/loader_test11 id11/),
1536
1537         (lc($self->{vendor}) ne 'informix' ?
1538             (q{ ALTER TABLE loader_test10 ADD CONSTRAINT loader_test11_fk } .
1539              q{ FOREIGN KEY (loader_test11) } .
1540              q{ REFERENCES loader_test11 (id11) })
1541         :
1542             (q{ ALTER TABLE loader_test10 ADD CONSTRAINT } .
1543              q{ FOREIGN KEY (loader_test11) } .
1544              q{ REFERENCES loader_test11 (id11) } .
1545              q{ CONSTRAINT loader_test11_fk })
1546         ),
1547     );
1548
1549     @statements_advanced_sqlite = (
1550         qq{
1551             CREATE TABLE loader_test10 (
1552                 id10 $self->{auto_inc_pk},
1553                 subject VARCHAR(8)
1554             ) $self->{innodb}
1555         },
1556         $make_auto_inc->(qw/loader_test10 id10/),
1557
1558         qq{
1559             CREATE TABLE loader_test11 (
1560                 id11 $self->{auto_inc_pk},
1561                 a_message VARCHAR(8) DEFAULT 'foo',
1562                 loader_test10 INTEGER $self->{null},
1563                 FOREIGN KEY (loader_test10) REFERENCES loader_test10 (id10)
1564             ) $self->{innodb}
1565         },
1566         $make_auto_inc->(qw/loader_test11 id11/),
1567
1568         (q{ ALTER TABLE loader_test10 ADD COLUMN } .
1569          q{ loader_test11 INTEGER REFERENCES loader_test11 (id11) }),
1570     );
1571
1572     @statements_inline_rels = (
1573         qq{
1574             CREATE TABLE loader_test12 (
1575                 id INTEGER NOT NULL PRIMARY KEY,
1576                 id2 VARCHAR(8) NOT NULL UNIQUE,
1577                 dat VARCHAR(8) NOT NULL UNIQUE
1578             ) $self->{innodb}
1579         },
1580
1581         q{ INSERT INTO loader_test12 (id,id2,dat) VALUES (1,'aaa','bbb') },
1582
1583         qq{
1584             CREATE TABLE loader_test13 (
1585                 id INTEGER NOT NULL PRIMARY KEY REFERENCES loader_test12,
1586                 loader_test12 VARCHAR(8) NOT NULL REFERENCES loader_test12 (id2),
1587                 dat VARCHAR(8) REFERENCES loader_test12 (dat)
1588             ) $self->{innodb}
1589         },
1590
1591         (q{ INSERT INTO loader_test13 (id,loader_test12,dat) } .
1592          q{ VALUES (1,'aaa','bbb') }),
1593     );
1594
1595
1596     @statements_implicit_rels = (
1597         qq{
1598             CREATE TABLE loader_test14 (
1599                 id INTEGER NOT NULL PRIMARY KEY,
1600                 dat VARCHAR(8)
1601             ) $self->{innodb}
1602         },
1603  
1604         q{ INSERT INTO loader_test14 (id,dat) VALUES (123,'aaa') },
1605
1606         qq{
1607             CREATE TABLE loader_test15 (
1608                 id INTEGER NOT NULL PRIMARY KEY,
1609                 loader_test14 INTEGER NOT NULL,
1610                 FOREIGN KEY (loader_test14) REFERENCES loader_test14
1611             ) $self->{innodb}
1612         },
1613
1614         q{ INSERT INTO loader_test15 (id,loader_test14) VALUES (1,123) },
1615     );
1616
1617     $self->drop_tables;
1618
1619     my $dbh = $self->dbconnect(1);
1620
1621     $dbh->do($_) for @{ $self->{pre_create} || [] };
1622
1623     $dbh->do($_) foreach (@statements);
1624
1625     if (not ($self->{vendor} eq 'mssql' && $dbh->{Driver}{Name} eq 'Sybase')) {
1626         $dbh->do($_) foreach (@{ $self->{data_type_tests}{ddl} || [] });
1627     }
1628
1629     unless($self->{skip_rels}) {
1630         # hack for now, since DB2 doesn't like inline comments, and we need
1631         # to test one for mysql, which works on everyone else...
1632         # this all needs to be refactored anyways.
1633         $dbh->do($_) for (@statements_reltests);
1634         if($self->{vendor} =~ /sqlite/i) {
1635             $dbh->do($_) for (@statements_advanced_sqlite);
1636         }
1637         else {
1638             $dbh->do($_) for (@statements_advanced);
1639         }
1640         unless($self->{no_inline_rels}) {
1641             $dbh->do($_) for (@statements_inline_rels);
1642         }
1643         unless($self->{no_implicit_rels}) {
1644             $dbh->do($_) for (@statements_implicit_rels);
1645         }
1646     }
1647
1648     $dbh->do($_) for @{ $self->{extra}->{create} || [] };
1649     $dbh->disconnect();
1650 }
1651
1652 sub drop_tables {
1653     my $self = shift;
1654
1655     my @tables = qw/
1656         loader_test1
1657         loader_test1s
1658         loader_test2
1659         LOADER_test23
1660         LoAdEr_test24
1661         loader_test35
1662         loader_test36
1663     /;
1664     
1665     my @tables_auto_inc = (
1666         [ qw/loader_test1s id/ ],
1667         [ qw/loader_test2 id/ ],
1668     );
1669
1670     my @tables_reltests = qw/
1671         loader_test4
1672         loader_test3
1673         loader_test6
1674         loader_test5
1675         loader_test8
1676         loader_test7
1677         loader_test9
1678         loader_test17
1679         loader_test16
1680         loader_test20
1681         loader_test19
1682         loader_test18
1683         loader_test22
1684         loader_test21
1685         loader_test26
1686         loader_test25
1687         loader_test28
1688         loader_test29
1689         loader_test27
1690         loader_test32
1691         loader_test31
1692         loader_test34
1693         loader_test33
1694     /;
1695
1696     my @tables_advanced = qw/
1697         loader_test11
1698         loader_test10
1699     /;
1700     
1701     my @tables_advanced_auto_inc = (
1702         [ qw/loader_test10 id10/ ],
1703         [ qw/loader_test11 id11/ ],
1704     );
1705
1706     my @tables_inline_rels = qw/
1707         loader_test13
1708         loader_test12
1709     /;
1710
1711     my @tables_implicit_rels = qw/
1712         loader_test15
1713         loader_test14
1714     /;
1715
1716     my @tables_rescan = qw/ loader_test30 /;
1717
1718     my @tables_preserve_case_tests = qw/ LoaderTest41 LoaderTest40 /;
1719
1720     my $drop_fk_mysql =
1721         q{ALTER TABLE loader_test10 DROP FOREIGN KEY loader_test11_fk};
1722
1723     my $drop_fk =
1724         q{ALTER TABLE loader_test10 DROP CONSTRAINT loader_test11_fk};
1725
1726     # For some reason some tests do this twice (I guess dependency issues?)
1727     # do it twice for all drops
1728     for (1,2) {
1729         my $dbh = $self->dbconnect(0);
1730
1731         $dbh->do($_) for @{ $self->{extra}{pre_drop_ddl} || [] };
1732
1733         $dbh->do("DROP TABLE $_") for @{ $self->{extra}{drop} || [] };
1734
1735         my $drop_auto_inc = $self->{auto_inc_drop_cb} || sub {};
1736
1737         unless($self->{skip_rels}) {
1738             $dbh->do("DROP TABLE $_") for (@tables_reltests);
1739             $dbh->do("DROP TABLE $_") for (@tables_reltests);
1740             if($self->{vendor} =~ /mysql/i) {
1741                 $dbh->do($drop_fk_mysql);
1742             }
1743             else {
1744                 $dbh->do($drop_fk);
1745             }
1746             $dbh->do($_) for map { $drop_auto_inc->(@$_) } @tables_advanced_auto_inc;
1747             $dbh->do("DROP TABLE $_") for (@tables_advanced);
1748
1749             unless($self->{no_inline_rels}) {
1750                 $dbh->do("DROP TABLE $_") for (@tables_inline_rels);
1751             }
1752             unless($self->{no_implicit_rels}) {
1753                 $dbh->do("DROP TABLE $_") for (@tables_implicit_rels);
1754             }
1755         }
1756         $dbh->do($_) for map { $drop_auto_inc->(@$_) } @tables_auto_inc;
1757         $dbh->do("DROP TABLE $_") for (@tables, @tables_rescan);
1758
1759         if (not ($self->{vendor} eq 'mssql' && $dbh->{Driver}{Name} eq 'Sybase')) {
1760             foreach my $data_type_table (@{ $self->{data_type_tests}{table_names} || [] }) {
1761                 $dbh->do("DROP TABLE $data_type_table");
1762             }
1763         }
1764
1765         my ($oqt, $cqt) = $self->get_oqt_cqt(always => 1);
1766
1767         $dbh->do("DROP TABLE ${oqt}${_}${cqt}") for @tables_preserve_case_tests;
1768
1769         $dbh->disconnect;
1770     }
1771 }
1772
1773 sub _custom_column_info {
1774     my ( $table_name, $column_name, $column_info ) = @_;
1775
1776     $table_name = lc ( $table_name );
1777     $column_name = lc ( $column_name );
1778
1779     if ( $table_name eq 'loader_test35' 
1780         and $column_name eq 'an_int' 
1781     ){
1782         return { is_numeric => 1 }
1783     }
1784     # Set inflate_datetime or  inflate_date to check 
1785     #   datetime_timezone and datetime_locale
1786     if ( $table_name eq 'loader_test36' ){
1787         return { inflate_datetime => 1 } if 
1788             ( $column_name eq 'b_char_as_data' );
1789         return { inflate_date => 1 } if 
1790             ( $column_name eq 'c_char_as_data' );
1791     }
1792
1793     return;
1794 }
1795
1796 my %DATA_TYPE_MULTI_TABLE_OVERRIDES = (
1797     oracle => qr/\blong\b/i,
1798     mssql  => qr/\b(?:timestamp|rowversion)\b/i,
1799     informix => qr/\b(?:bigserial|serial8)\b/i,
1800 );
1801
1802 sub setup_data_type_tests {
1803     my $self = shift;
1804
1805     return unless my $types = $self->{data_types};
1806
1807     my $tests = $self->{data_type_tests} = {};
1808
1809     # split types into tables based on overrides
1810     my (@types, @split_off_types, @first_table_types);
1811     {
1812         my $split_off_re = $DATA_TYPE_MULTI_TABLE_OVERRIDES{lc($self->{vendor})} || qr/(?!)/;
1813
1814         @types = keys %$types;
1815         @split_off_types   = grep  /$split_off_re/, @types;
1816         @first_table_types = grep !/$split_off_re/, @types;
1817     }
1818
1819     @types = +{ map +($_, $types->{$_}), @first_table_types },
1820         map +{ $_, $types->{$_} }, @split_off_types;
1821
1822     my $test_count = 0;
1823     my $table_num  = 10000;
1824
1825     foreach my $types (@types) {
1826         my $table_name    = "loader_test$table_num";
1827         push @{ $tests->{table_names} }, $table_name;
1828
1829         my $table_moniker = "LoaderTest$table_num";
1830         push @{ $tests->{table_monikers} }, $table_moniker;
1831
1832         $table_num++;
1833
1834         my $cols = $tests->{columns}{$table_moniker} = {};
1835
1836         my $ddl = "CREATE TABLE $table_name (\n    id INTEGER NOT NULL PRIMARY KEY,\n";
1837
1838         my %seen_col_names;
1839
1840         while (my ($col_def, $expected_info) = each %$types) {
1841             (my $type_alias = $col_def) =~ s/\( ([^)]+) \)//xg;
1842
1843             my $size = $1;
1844             $size = '' unless defined $size;
1845             $size =~ s/\s+//g;
1846             my @size = split /,/, $size;
1847
1848             # some DBs don't like very long column names
1849             if ($self->{vendor} =~ /^(?:firebird|sqlanywhere|oracle|db2)\z/i) {
1850                 my ($col_def, $default) = $type_alias =~ /^(.*)(default.*)?\z/i;
1851
1852                 $type_alias = substr $col_def, 0, 15;
1853
1854                 $type_alias .= '_with_dflt' if $default;
1855             }
1856
1857             $type_alias =~ s/\s/_/g;
1858             $type_alias =~ s/\W//g;
1859
1860             my $col_name = 'col_' . $type_alias;
1861             
1862             if (@size) {
1863                 my $size_name = join '_', apply { s/\W//g } @size;
1864
1865                 $col_name .= "_sz_$size_name";
1866             }
1867
1868             # XXX would be better to check _loader->preserve_case
1869             $col_name = lc $col_name;
1870
1871             $col_name .= '_' . $seen_col_names{$col_name} if $seen_col_names{$col_name}++;
1872
1873             $ddl .= "    $col_name $col_def,\n";
1874
1875             $cols->{$col_name} = $expected_info;
1876
1877             $test_count++;
1878         }
1879
1880         $ddl =~ s/,\n\z/\n)/;
1881
1882         push @{ $tests->{ddl} }, $ddl;
1883     }
1884
1885     $tests->{test_count} = $test_count;
1886
1887     return $test_count;
1888 }
1889
1890 sub rescan_without_warnings {
1891     my ($self, $conn) = @_;
1892
1893     local $SIG{__WARN__} = sub { warn @_ unless $_[0] =~ RESCAN_WARNINGS };
1894     return $conn->rescan;
1895 }
1896
1897 sub test_column_accessor_map {
1898     my ( $column_name, $default_name, $context ) = @_;
1899     if( lc($column_name) eq 'crumb_crisp_coating' ) {
1900
1901         is( $default_name, 'crumb_crisp_coating', 'column_accessor_map was passed the default name' );
1902         ok( $context->{$_}, "column_accessor_map func was passed the $_" )
1903             for qw( table_name table_class table_moniker schema_class );
1904
1905         return 'trivet';
1906     } else {
1907         return $default_name;
1908     }
1909 }
1910
1911 sub DESTROY {
1912     my $self = shift;
1913     unless ($ENV{SCHEMA_LOADER_TESTS_NOCLEANUP}) {
1914       $self->drop_tables if $self->{_created};
1915       rmtree $DUMP_DIR
1916     }
1917 }
1918
1919 1;
1920 # vim:et sts=4 sw=4 tw=0: