make MySQL FK handling more robust
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / 10_02mysql_common.t
1 use strict;
2 use warnings;
3 use Test::More;
4 use Test::Exception;
5 use Try::Tiny;
6 use File::Path 'rmtree';
7 use DBIx::Class::Schema::Loader::Utils 'slurp_file';
8 use DBIx::Class::Schema::Loader 'make_schema_at';
9
10 use lib qw(t/lib);
11
12 use dbixcsl_common_tests;
13 use dbixcsl_test_dir '$tdir';
14
15 use constant EXTRA_DUMP_DIR => "$tdir/mysql_extra_dump";
16
17 my $dsn         = $ENV{DBICTEST_MYSQL_DSN} || '';
18 my $user        = $ENV{DBICTEST_MYSQL_USER} || '';
19 my $password    = $ENV{DBICTEST_MYSQL_PASS} || '';
20 my $test_innodb = $ENV{DBICTEST_MYSQL_INNODB} || 0;
21
22 my $skip_rels_msg = 'You need to set the environment variable DBICTEST_MYSQL_INNODB=1 to test relationships.';
23
24 my $innodb = $test_innodb ? q{Engine=InnoDB} : '';
25
26 my ($schema, $databases_created); # for cleanup in END for extra tests
27
28 my $tester = dbixcsl_common_tests->new(
29     vendor           => 'Mysql',
30     auto_inc_pk      => 'INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT',
31     innodb           => $innodb,
32     dsn              => $dsn,
33     user             => $user,
34     password         => $password,
35     connect_info_opts=> { on_connect_call => 'set_strict_mode' },
36     loader_options   => { preserve_case => 1 },
37     skip_rels        => $test_innodb ? 0 : $skip_rels_msg,
38     quote_char       => '`',
39     no_inline_rels   => 1,
40     no_implicit_rels => 1,
41     data_types  => {
42         # http://dev.mysql.com/doc/refman/5.5/en/data-type-overview.html
43         # Numeric Types
44         'bit'         => { data_type => 'bit', size => 1 },
45         'bit(11)'     => { data_type => 'bit', size => 11 },
46
47         'bool'        => { data_type => 'tinyint' },
48         'boolean'     => { data_type => 'tinyint' },
49         'tinyint'     => { data_type => 'tinyint' },
50         'tinyint unsigned'
51                       => { data_type => 'tinyint',   extra => { unsigned => 1 } },
52         'smallint'    => { data_type => 'smallint' },
53         'smallint unsigned'
54                       => { data_type => 'smallint',  extra => { unsigned => 1 } },
55         'mediumint'   => { data_type => 'mediumint' },
56         'mediumint unsigned'
57                       => { data_type => 'mediumint', extra => { unsigned => 1 } },
58         'int'         => { data_type => 'integer' },
59         'int unsigned'
60                       => { data_type => 'integer',   extra => { unsigned => 1 } },
61         'integer'     => { data_type => 'integer' },
62         'integer unsigned'
63                       => { data_type => 'integer',   extra => { unsigned => 1 } },
64         'integer not null'
65                       => { data_type => 'integer' },
66         'bigint'      => { data_type => 'bigint' },
67         'bigint unsigned'
68                       => { data_type => 'bigint',    extra => { unsigned => 1 } },
69
70         'serial'      => { data_type => 'bigint', is_auto_increment => 1, extra => { unsigned => 1 } },
71
72         'float'       => { data_type => 'float' },
73         'float unsigned'
74                       => { data_type => 'float',     extra => { unsigned => 1 } },
75         'double'      => { data_type => 'double precision' },
76         'double unsigned'
77                       => { data_type => 'double precision', extra => { unsigned => 1 } },
78         'double precision' =>
79                          { data_type => 'double precision' },
80         'double precision unsigned'
81                       => { data_type => 'double precision', extra => { unsigned => 1 } },
82
83         # we skip 'real' because its alias depends on the 'REAL AS FLOAT' setting
84
85         'float(2)'    => { data_type => 'float' },
86         'float(24)'   => { data_type => 'float' },
87         'float(25)'   => { data_type => 'double precision' },
88
89         'float(3,3)'  => { data_type => 'float', size => [3,3] },
90         'double(3,3)' => { data_type => 'double precision', size => [3,3] },
91         'double precision(3,3)'
92                       => { data_type => 'double precision', size => [3,3] },
93
94         'decimal'     => { data_type => 'decimal' },
95         'decimal unsigned'
96                       => { data_type => 'decimal', extra => { unsigned => 1 } },
97         'dec'         => { data_type => 'decimal' },
98         'numeric'     => { data_type => 'decimal' },
99         'fixed'       => { data_type => 'decimal' },
100
101         'decimal(3)'   => { data_type => 'decimal', size => [3,0] },
102
103         'decimal(3,3)' => { data_type => 'decimal', size => [3,3] },
104         'dec(3,3)'     => { data_type => 'decimal', size => [3,3] },
105         'numeric(3,3)' => { data_type => 'decimal', size => [3,3] },
106         'fixed(3,3)'   => { data_type => 'decimal', size => [3,3] },
107
108         # Date and Time Types
109         'date'        => { data_type => 'date', datetime_undef_if_invalid => 1 },
110         'datetime'    => { data_type => 'datetime', datetime_undef_if_invalid => 1 },
111         'timestamp default current_timestamp'
112                       => { data_type => 'timestamp', default_value => \'current_timestamp', datetime_undef_if_invalid => 1 },
113         'time'        => { data_type => 'time' },
114         'year'        => { data_type => 'year' },
115         'year(4)'     => { data_type => 'year' },
116         'year(2)'     => { data_type => 'year', size => 2 },
117
118         # String Types
119         'char'         => { data_type => 'char',      size => 1  },
120         'char(11)'     => { data_type => 'char',      size => 11 },
121         'varchar(20)'  => { data_type => 'varchar',   size => 20 },
122         'binary'       => { data_type => 'binary',    size => 1  },
123         'binary(11)'   => { data_type => 'binary',    size => 11 },
124         'varbinary(20)'=> { data_type => 'varbinary', size => 20 },
125
126         'tinyblob'    => { data_type => 'tinyblob' },
127         'tinytext'    => { data_type => 'tinytext' },
128         'blob'        => { data_type => 'blob' },
129
130         # text(M) types will map to the appropriate type, length is not stored
131         'text'        => { data_type => 'text' },
132
133         'mediumblob'  => { data_type => 'mediumblob' },
134         'mediumtext'  => { data_type => 'mediumtext' },
135         'longblob'    => { data_type => 'longblob' },
136         'longtext'    => { data_type => 'longtext' },
137
138         "enum('foo','bar','baz')"
139                       => { data_type => 'enum', extra => { list => [qw/foo bar baz/] } },
140         "enum('foo \\'bar\\' baz', 'foo ''bar'' quux')"
141                       => { data_type => 'enum', extra => { list => [q{foo 'bar' baz}, q{foo 'bar' quux}] } },
142         "set('foo \\'bar\\' baz', 'foo ''bar'' quux')"
143                       => { data_type => 'set', extra => { list => [q{foo 'bar' baz}, q{foo 'bar' quux}] } },
144         "set('foo','bar','baz')"
145                       => { data_type => 'set',  extra => { list => [qw/foo bar baz/] } },
146
147         # RT#68717
148         "enum('11,10 (<500)/0 DUN','4,90 (<120)/0 EUR') NOT NULL default '11,10 (<500)/0 DUN'"
149                       => { data_type => 'enum', extra => { list => ['11,10 (<500)/0 DUN', '4,90 (<120)/0 EUR'] }, default_value => '11,10 (<500)/0 DUN' },
150         "set('11_10 (<500)/0 DUN','4_90 (<120)/0 EUR') NOT NULL default '11_10 (<500)/0 DUN'"
151                       => { data_type => 'set', extra => { list => ['11_10 (<500)/0 DUN', '4_90 (<120)/0 EUR'] }, default_value => '11_10 (<500)/0 DUN' },
152         "enum('19,90 (<500)/0 EUR','4,90 (<120)/0 EUR','7,90 (<200)/0 CHF','300 (<6000)/0 CZK','4,90 (<100)/0 EUR','39 (<900)/0 DKK','299 (<5000)/0 EEK','9,90 (<250)/0 EUR','3,90 (<100)/0 GBP','3000 (<70000)/0 HUF','4000 (<70000)/0 JPY','13,90 (<200)/0 LVL','99 (<2500)/0 NOK','39 (<1000)/0 PLN','1000 (<20000)/0 RUB','49 (<2500)/0 SEK','29 (<600)/0 USD','19,90 (<600)/0 EUR','0 EUR','0 CHF') NOT NULL default '19,90 (<500)/0 EUR'"
153                       => { data_type => 'enum', extra => { list => ['19,90 (<500)/0 EUR','4,90 (<120)/0 EUR','7,90 (<200)/0 CHF','300 (<6000)/0 CZK','4,90 (<100)/0 EUR','39 (<900)/0 DKK','299 (<5000)/0 EEK','9,90 (<250)/0 EUR','3,90 (<100)/0 GBP','3000 (<70000)/0 HUF','4000 (<70000)/0 JPY','13,90 (<200)/0 LVL','99 (<2500)/0 NOK','39 (<1000)/0 PLN','1000 (<20000)/0 RUB','49 (<2500)/0 SEK','29 (<600)/0 USD','19,90 (<600)/0 EUR','0 EUR','0 CHF'] }, default_value => '19,90 (<500)/0 EUR' },
154     },
155     extra => {
156         create => [
157             qq{
158                 CREATE TABLE `mysql_loader-test1` (
159                     id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'The\15\12Column',
160                     value varchar(100)
161                 ) $innodb COMMENT 'The\15\12Table'
162             },
163             q{
164                 CREATE VIEW mysql_loader_test2 AS SELECT * FROM `mysql_loader-test1`
165             },
166             # RT#68717
167             qq{
168                 CREATE TABLE `mysql_loader_test3` (
169                   `ISO3_code` char(3) NOT NULL default '',
170                   `lang_pref` enum('de','en','fr','nl','dk','es','se') NOT NULL,
171                   `vat` decimal(4,2) default '16.00',
172                   `price_group` enum('EUR_DEFAULT','GBP_GBR','EUR_AUT_BEL_FRA_IRL_NLD','EUR_DNK_SWE','EUR_AUT','EUR_BEL','EUR_FIN','EUR_FRA','EUR_IRL','EUR_NLD','EUR_DNK','EUR_POL','EUR_PRT','EUR_SWE','CHF_CHE','DKK_DNK','SEK_SWE','NOK_NOR','USD_USA','CZK_CZE','PLN_POL','RUB_RUS','HUF_HUN','SKK_SVK','JPY_JPN','LVL_LVA','ROL_ROU','EEK_EST') NOT NULL default 'EUR_DEFAULT',
173                   `del_group` enum('19,90 (<500)/0 EUR','4,90 (<120)/0 EUR','7,90 (<200)/0 CHF','300 (<6000)/0 CZK','4,90 (<100)/0 EUR','39 (<900)/0 DKK','299 (<5000)/0 EEK','9,90 (<250)/0 EUR','3,90 (<100)/0 GBP','3000 (<70000)/0 HUF','4000 (<70000)/0 JPY','13,90 (<200)/0 LVL','99 (<2500)/0 NOK','39 (<1000)/0 PLN','1000 (<20000)/0 RUB','49 (<2500)/0 SEK','29 (<600)/0 USD','19,90 (<600)/0 EUR','0 EUR','0 CHF') NOT NULL default '19,90 (<500)/0 EUR',
174                   `express_del_group` enum('NO','39 EUR (EXPRESS)','59 EUR (EXPRESS)','79 CHF (EXPRESS)','49 EUR (EXPRESS)','990 CZK (EXPRESS)','19,9 EUR (EXPRESS)','290 DKK (EXPRESS)','990 EEK (EXPRESS)','39 GBP (EXPRESS)','14000 HUF (EXPRESS)','49 LVL (EXPRESS)','590 NOK (EXPRESS)','250 PLN (EXPRESS)','490 SEK (EXPRESS)') NOT NULL default 'NO',
175                   `pmethod` varchar(255) NOT NULL default 'VISA,MASTER',
176                   `delivery_time` varchar(5) default NULL,
177                   `express_delivery_time` varchar(5) default NULL,
178                   `eu` int(1) default '0',
179                   `cod_costs` varchar(12) default NULL,
180                   PRIMARY KEY (`ISO3_code`)
181                 ) $innodb
182             },
183         ],
184         pre_drop_ddl => [ 'DROP VIEW mysql_loader_test2', ],
185         drop => [ 'mysql_loader-test1', 'mysql_loader_test3' ],
186         count => 5 + 28 * 2,
187         run => sub {
188             my ($monikers, $classes);
189             ($schema, $monikers, $classes) = @_;
190
191             is $monikers->{'mysql_loader-test1'}, 'MysqlLoaderTest1',
192                 'table with dash correctly monikerized';
193
194             my $rsrc = $schema->source('MysqlLoaderTest2');
195
196             is $rsrc->column_info('value')->{data_type}, 'varchar',
197                 'view introspected successfully';
198
199             $rsrc = $schema->source('MysqlLoaderTest3');
200
201             is_deeply $rsrc->column_info('del_group')->{extra}{list}, ['19,90 (<500)/0 EUR','4,90 (<120)/0 EUR','7,90 (<200)/0 CHF','300 (<6000)/0 CZK','4,90 (<100)/0 EUR','39 (<900)/0 DKK','299 (<5000)/0 EEK','9,90 (<250)/0 EUR','3,90 (<100)/0 GBP','3000 (<70000)/0 HUF','4000 (<70000)/0 JPY','13,90 (<200)/0 LVL','99 (<2500)/0 NOK','39 (<1000)/0 PLN','1000 (<20000)/0 RUB','49 (<2500)/0 SEK','29 (<600)/0 USD','19,90 (<600)/0 EUR','0 EUR','0 CHF'],
202                 'hairy enum introspected correctly';
203
204             my $class    = $classes->{'mysql_loader-test1'};
205             my $filename = $schema->loader->get_dump_filename($class);
206
207             my $code = slurp_file $filename;
208
209             like $code, qr/^=head1 NAME\n\n^$class - The\nTable\n\n^=cut\n/m,
210                 'table comment';
211
212             like $code, qr/^=head2 id\n\n(.+:.+\n)+\nThe\nColumn\n\n/m,
213                 'column comment and attrs';
214
215             SKIP: {
216                 my $dbh = $schema->storage->dbh;
217
218                 try {
219                     $dbh->do('CREATE DATABASE `dbicsl-test`');
220                 }
221                 catch {
222                     skip "no CREATE DATABASE privileges", 28 * 2;
223                 };
224
225                 $dbh->do(<<"EOF");
226                     CREATE TABLE `dbicsl-test`.mysql_loader_test4 (
227                         id INT AUTO_INCREMENT PRIMARY KEY,
228                         value VARCHAR(100)
229                     ) $innodb
230 EOF
231                 $dbh->do(<<"EOF");
232                     CREATE TABLE `dbicsl-test`.mysql_loader_test5 (
233                         id INT AUTO_INCREMENT PRIMARY KEY,
234                         value VARCHAR(100),
235                         four_id INTEGER UNIQUE,
236                         FOREIGN KEY (four_id) REFERENCES `dbicsl-test`.mysql_loader_test4 (id)
237                     ) $innodb
238 EOF
239                 $dbh->do('CREATE DATABASE `dbicsl.test`');
240                 $dbh->do(<<"EOF");
241                     CREATE TABLE `dbicsl.test`.mysql_loader_test6 (
242                         id INT AUTO_INCREMENT PRIMARY KEY,
243                         value VARCHAR(100),
244                         mysql_loader_test4_id INTEGER,
245                         FOREIGN KEY (mysql_loader_test4_id) REFERENCES `dbicsl-test`.mysql_loader_test4 (id)
246                     ) $innodb
247 EOF
248                 $dbh->do(<<"EOF");
249                     CREATE TABLE `dbicsl.test`.mysql_loader_test7 (
250                         id INT AUTO_INCREMENT PRIMARY KEY,
251                         value VARCHAR(100),
252                         six_id INTEGER UNIQUE,
253                         FOREIGN KEY (six_id) REFERENCES `dbicsl.test`.mysql_loader_test6 (id)
254                     ) $innodb
255 EOF
256                 $dbh->do(<<"EOF");
257                     CREATE TABLE `dbicsl-test`.mysql_loader_test8 (
258                         id INT AUTO_INCREMENT PRIMARY KEY,
259                         value VARCHAR(100),
260                         mysql_loader_test7_id INTEGER,
261                         FOREIGN KEY (mysql_loader_test7_id) REFERENCES `dbicsl.test`.mysql_loader_test7 (id)
262                     ) $innodb
263 EOF
264                 # Test dumping a rel to a table that's not part of the dump.
265                 $dbh->do('CREATE DATABASE `dbicsl_test_ignored`');
266                 $dbh->do(<<"EOF");
267                     CREATE TABLE `dbicsl_test_ignored`.mysql_loader_test9 (
268                         id INT AUTO_INCREMENT PRIMARY KEY,
269                         value VARCHAR(100)
270                     ) $innodb
271 EOF
272                 $dbh->do(<<"EOF");
273                     CREATE TABLE `dbicsl-test`.mysql_loader_test10 (
274                         id INT AUTO_INCREMENT PRIMARY KEY,
275                         value VARCHAR(100),
276                         mysql_loader_test9_id INTEGER,
277                         FOREIGN KEY (mysql_loader_test9_id) REFERENCES `dbicsl_test_ignored`.mysql_loader_test9 (id)
278                     ) $innodb
279 EOF
280
281                 $databases_created = 1;
282
283                 SKIP: foreach my $db_schema (['dbicsl-test', 'dbicsl.test'], '%') {
284                     if ($db_schema eq '%') {
285                         try {
286                             $dbh->selectall_arrayref('SHOW DATABASES');
287                         }
288                         catch {
289                             skip 'no SHOW DATABASES privileges', 28;
290                         }
291                     }
292
293                     lives_and {
294                         rmtree EXTRA_DUMP_DIR;
295
296                         my @warns;
297                         local $SIG{__WARN__} = sub {
298                             push @warns, $_[0] unless $_[0] =~ /\bcollides\b/;
299                         };
300
301                         make_schema_at(
302                             'MySQLMultiSchema',
303                             {
304                                 naming => 'current',
305                                 db_schema => $db_schema,
306                                 dump_directory => EXTRA_DUMP_DIR,
307                                 quiet => 1,
308                             },
309                             [ $dsn, $user, $password ],
310                         );
311
312                         diag join "\n", @warns if @warns;
313
314                         is @warns, 0;
315                     } 'dumped schema for "dbicsl-test" and "dbicsl.test" databases with no warnings';
316
317                     my ($test_schema, $rsrc, $rs, $row, %uniqs, $rel_info);
318
319                     lives_and {
320                         ok $test_schema = MySQLMultiSchema->connect($dsn, $user, $password);
321                     } 'connected test schema';
322
323                     lives_and {
324                         ok $rsrc = $test_schema->source('MysqlLoaderTest4');
325                     } 'got source for table in database name with dash';
326
327                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
328                         'column in database name with dash';
329
330                     is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
331                         'column in database name with dash';
332
333                     is try { $rsrc->column_info('value')->{size} }, 100,
334                         'column in database name with dash';
335
336                     lives_and {
337                         ok $rs = $test_schema->resultset('MysqlLoaderTest4');
338                     } 'got resultset for table in database name with dash';
339
340                     lives_and {
341                         ok $row = $rs->create({ value => 'foo' });
342                     } 'executed SQL on table in database name with dash';
343
344                     SKIP: {
345                         skip 'set the environment variable DBICTEST_MYSQL_INNODB=1 to test relationships', 3 unless $test_innodb;
346
347                         $rel_info = try { $rsrc->relationship_info('mysql_loader_test5') };
348
349                         is_deeply $rel_info->{cond}, {
350                             'foreign.four_id' => 'self.id'
351                         }, 'relationship in database name with dash';
352
353                         is $rel_info->{attrs}{accessor}, 'single',
354                             'relationship in database name with dash';
355
356                         is $rel_info->{attrs}{join_type}, 'LEFT',
357                             'relationship in database name with dash';
358                     }
359
360                     lives_and {
361                         ok $rsrc = $test_schema->source('MysqlLoaderTest5');
362                     } 'got source for table in database name with dash';
363
364                     %uniqs = try { $rsrc->unique_constraints };
365
366                     is keys %uniqs, 2,
367                         'got unique and primary constraint in database name with dash';
368
369                     lives_and {
370                         ok $rsrc = $test_schema->source('MysqlLoaderTest6');
371                     } 'got source for table in database name with dot';
372
373                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
374                         'column in database name with dot introspected correctly';
375
376                     is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
377                         'column in database name with dot introspected correctly';
378
379                     is try { $rsrc->column_info('value')->{size} }, 100,
380                         'column in database name with dot introspected correctly';
381
382                     lives_and {
383                         ok $rs = $test_schema->resultset('MysqlLoaderTest6');
384                     } 'got resultset for table in database name with dot';
385
386                     lives_and {
387                         ok $row = $rs->create({ value => 'foo' });
388                     } 'executed SQL on table in database name with dot';
389
390                     SKIP: {
391                         skip 'set the environment variable DBICTEST_MYSQL_INNODB=1 to test relationships', 3 unless $test_innodb;
392
393                         $rel_info = try { $rsrc->relationship_info('mysql_loader_test7') };
394
395                         is_deeply $rel_info->{cond}, {
396                             'foreign.six_id' => 'self.id'
397                         }, 'relationship in database name with dot';
398
399                         is $rel_info->{attrs}{accessor}, 'single',
400                             'relationship in database name with dot';
401
402                         is $rel_info->{attrs}{join_type}, 'LEFT',
403                             'relationship in database name with dot';
404                     }
405
406                     lives_and {
407                         ok $rsrc = $test_schema->source('MysqlLoaderTest7');
408                     } 'got source for table in database name with dot';
409
410                     %uniqs = try { $rsrc->unique_constraints };
411
412                     is keys %uniqs, 2,
413                         'got unique and primary constraint in database name with dot';
414
415                     SKIP: {
416                         skip 'set the environment variable DBICTEST_MYSQL_INNODB=1 to test relationships', 4 unless $test_innodb;
417
418                         lives_and {
419                             ok $test_schema->source('MysqlLoaderTest6')
420                                 ->has_relationship('mysql_loader_test4');
421                         } 'cross-database relationship in multi-db_schema';
422
423                         lives_and {
424                             ok $test_schema->source('MysqlLoaderTest4')
425                                 ->has_relationship('mysql_loader_test6s');
426                         } 'cross-database relationship in multi-db_schema';
427
428                         lives_and {
429                             ok $test_schema->source('MysqlLoaderTest8')
430                                 ->has_relationship('mysql_loader_test7');
431                         } 'cross-database relationship in multi-db_schema';
432
433                         lives_and {
434                             ok $test_schema->source('MysqlLoaderTest7')
435                                 ->has_relationship('mysql_loader_test8s');
436                         } 'cross-database relationship in multi-db_schema';
437                     }
438                 }
439             }
440         },
441     },
442 );
443
444 if( !$dsn || !$user ) {
445     $tester->skip_tests('You need to set the DBICTEST_MYSQL_DSN, DBICTEST_MYSQL_USER, and DBICTEST_MYSQL_PASS environment variables');
446 }
447 else {
448     diag $skip_rels_msg if not $test_innodb;
449     $tester->run_tests();
450 }
451
452 END {
453     if (not $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP}) {
454         if ($databases_created && (my $dbh = try { $schema->storage->dbh })) {
455             foreach my $table ('`dbicsl-test`.mysql_loader_test10',
456                                'dbicsl_test_ignored.mysql_loader_test9',
457                                '`dbicsl-test`.mysql_loader_test8',
458                                '`dbicsl.test`.mysql_loader_test7',
459                                '`dbicsl.test`.mysql_loader_test6',
460                                '`dbicsl-test`.mysql_loader_test5',
461                                '`dbicsl-test`.mysql_loader_test4') {
462                 try {
463                     $dbh->do("DROP TABLE $table");
464                 }
465                 catch {
466                     diag "Error dropping table: $_";
467                 };
468             }
469
470             foreach my $db (qw/dbicsl-test dbicsl.test dbicsl_test_ignored/) {
471                 try {
472                     $dbh->do("DROP DATABASE `$db`");
473                 }
474                 catch {
475                     diag "Error dropping test database $db: $_";
476                 };
477             }
478         }
479         rmtree EXTRA_DUMP_DIR;
480     }
481 }
482 # vim:et sts=4 sw=4 tw=0: