3217ef5275b3cdfc76c333482b56d7febce43aad
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / 10_10informix_common.t
1 use DBIx::Class::Schema::Loader::Optional::Dependencies
2     -skip_all_without => 'test_rdbms_informix';
3
4 use strict;
5 use warnings;
6 use Test::More;
7 use Test::Exception;
8 use Try::Tiny;
9 use File::Path 'rmtree';
10 use DBIx::Class::Schema::Loader 'make_schema_at';
11 use DBIx::Class::Schema::Loader::Utils 'split_name';
12 use String::ToIdentifier::EN::Unicode 'to_identifier';
13 use namespace::clean;
14
15 use lib qw(t/lib);
16
17 use dbixcsl_common_tests ();
18 use dbixcsl_test_dir '$tdir';
19
20 use constant EXTRA_DUMP_DIR => "$tdir/informix_extra_dump";
21
22 # to support " quoted identifiers
23 BEGIN { $ENV{DELIMIDENT} = 'y' }
24
25 # This test doesn't run over a shared memory connection, because of the single connection limit.
26
27 my $dsn      = $ENV{DBICTEST_INFORMIX_DSN} || '';
28 my $user     = $ENV{DBICTEST_INFORMIX_USER} || '';
29 my $password = $ENV{DBICTEST_INFORMIX_PASS} || '';
30
31 my ($schema, $extra_schema); # for cleanup in END for extra tests
32
33 dbixcsl_common_tests->new(
34     vendor         => 'Informix',
35     auto_inc_pk    => 'serial primary key',
36     null           => '',
37     default_function     => 'current year to fraction(5)',
38     default_function_def => 'datetime year to fraction(5) default current year to fraction(5)',
39     dsn            => $dsn,
40     user           => $user,
41     password       => $password,
42     loader_options => { preserve_case => 1 },
43     quote_char     => '"',
44     data_types => {
45         # http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=/com.ibm.sqlr.doc/ids_sqr_094.htm
46
47         # Numeric Types
48         'int'              => { data_type => 'integer' },
49         integer            => { data_type => 'integer' },
50         int8               => { data_type => 'bigint' },
51         bigint             => { data_type => 'bigint' },
52         serial             => { data_type => 'integer', is_auto_increment => 1 },
53         bigserial          => { data_type => 'bigint',  is_auto_increment => 1 },
54         serial8            => { data_type => 'bigint',  is_auto_increment => 1 },
55         smallint           => { data_type => 'smallint' },
56         real               => { data_type => 'real' },
57         smallfloat         => { data_type => 'real' },
58         # just 'double' is a syntax error
59         'double precision' => { data_type => 'double precision' },
60         float              => { data_type => 'double precision' },
61         'float(1)'         => { data_type => 'double precision' },
62         'float(5)'         => { data_type => 'double precision' },
63         'float(10)'        => { data_type => 'double precision' },
64         'float(15)'        => { data_type => 'double precision' },
65         'float(16)'        => { data_type => 'double precision' },
66         numeric            => { data_type => 'numeric' },
67         decimal            => { data_type => 'numeric' },
68         dec                => { data_type => 'numeric' },
69         'numeric(6,3)'     => { data_type => 'numeric', size => [6,3] },
70         'decimal(6,3)'     => { data_type => 'numeric', size => [6,3] },
71         'dec(6,3)'         => { data_type => 'numeric', size => [6,3] },
72
73         # Boolean Type
74         # XXX this should map to 'boolean'
75         boolean            => { data_type => 'smallint' },
76
77         # Money Type
78         money              => { data_type => 'money' },
79         'money(3,3)'       => { data_type => 'numeric', size => [3,3] },
80
81         # Byte Type
82         byte               => { data_type => 'bytea', original => { data_type => 'byte' } },
83
84         # Character String Types
85         char               => { data_type => 'char', size => 1 },
86         'char(3)'          => { data_type => 'char', size => 3 },
87         character          => { data_type => 'char', size => 1 },
88         'character(3)'     => { data_type => 'char', size => 3 },
89         'varchar(3)'       => { data_type => 'varchar', size => 3 },
90         'character varying(3)'
91                            => { data_type => 'varchar', size => 3 },
92         # XXX min size not supported, colmin from syscolumns is NULL
93         'varchar(3,2)'     => { data_type => 'varchar', size => 3 },
94         'character varying(3,2)'
95                            => { data_type => 'varchar', size => 3 },
96         nchar              => { data_type => 'nchar', size => 1 },
97         'nchar(3)'         => { data_type => 'nchar', size => 3 },
98         'nvarchar(3)'      => { data_type => 'nvarchar', size => 3 },
99         'nvarchar(3,2)'    => { data_type => 'nvarchar', size => 3 },
100         'lvarchar(3)'      => { data_type => 'lvarchar', size => 3 },
101         'lvarchar(33)'     => { data_type => 'lvarchar', size => 33 },
102         text               => { data_type => 'text' },
103
104         # DateTime Types
105         date               => { data_type => 'date' },
106         'date default today'
107                            => { data_type => 'date', default_value => \'today' },
108         # XXX support all precisions
109         'datetime year to fraction(5)',
110                            => { data_type => 'datetime year to fraction(5)' },
111         'datetime year to fraction(5) default current year to fraction(5)',
112                            => { data_type => 'datetime year to fraction(5)', default_value => \'current year to fraction(5)' },
113         # XXX do interval
114
115         # Blob Types
116         # XXX no way to distinguish opaque types boolean, blob and clob
117         blob               => { data_type => 'blob' },
118         clob               => { data_type => 'blob' },
119
120         # IDSSECURITYLABEL Type
121         #
122         # This requires the DBSECADM privilege and a security policy on the
123         # table, things I know nothing about.
124 #        idssecuritylabel   => { data_type => 'idssecuritylabel' },
125
126         # List Types
127         # XXX need to introspect element type too
128         'list(varchar(20) not null)'
129                            => { data_type => 'list' },
130         'multiset(varchar(20) not null)'
131                            => { data_type => 'multiset' },
132         'set(varchar(20) not null)'
133                            => { data_type => 'set' },
134     },
135     extra => {
136         count => 26 * 2,
137         run   => sub {
138             ($schema) = @_;
139
140             SKIP: {
141                 skip 'Set the DBICTEST_INFORMIX_EXTRADB_DSN, _USER and _PASS environment variables to run the multi-database tests', 26 * 2
142                     unless $ENV{DBICTEST_INFORMIX_EXTRADB_DSN};
143
144                 $extra_schema = $schema->clone;
145                 $extra_schema->connection(@ENV{map "DBICTEST_INFORMIX_EXTRADB_$_",
146                     qw/DSN USER PASS/
147                 });
148
149                 my $dbh1 = $schema->storage->dbh;
150
151                 $dbh1->do(<<'EOF');
152                     CREATE TABLE informix_loader_test4 (
153                         id SERIAL PRIMARY KEY,
154                         value VARCHAR(100)
155                     )
156 EOF
157                 $dbh1->do(<<'EOF');
158                     CREATE TABLE informix_loader_test5 (
159                         id SERIAL PRIMARY KEY,
160                         value VARCHAR(100),
161                         four_id INTEGER REFERENCES informix_loader_test4 (id)
162                     )
163 EOF
164                 $dbh1->do(<<'EOF');
165 ALTER TABLE informix_loader_test5 ADD CONSTRAINT UNIQUE (four_id) CONSTRAINT loader_test5_uniq
166 EOF
167
168                 my $db1 = db_name($schema);
169
170                 $dbh1->disconnect;
171
172                 my $dbh2 = $extra_schema->storage->dbh;
173
174                 $dbh2->do(<<'EOF');
175                     CREATE TABLE informix_loader_test5 (
176                         pk SERIAL PRIMARY KEY,
177                         value VARCHAR(100),
178                         four_id INTEGER
179                     )
180 EOF
181                 $dbh2->do(<<'EOF');
182 ALTER TABLE informix_loader_test5 ADD CONSTRAINT UNIQUE (four_id) CONSTRAINT loader_test5_uniq
183 EOF
184                 $dbh2->do(<<"EOF");
185                     CREATE TABLE informix_loader_test6 (
186                         id SERIAL PRIMARY KEY,
187                         value VARCHAR(100)
188                     )
189 EOF
190                 $dbh2->do(<<"EOF");
191                     CREATE TABLE informix_loader_test7 (
192                         id SERIAL PRIMARY KEY,
193                         value VARCHAR(100),
194                         six_id INTEGER UNIQUE REFERENCES informix_loader_test6 (id)
195                     )
196 EOF
197
198                 my $db2 = db_name($extra_schema);
199
200                 $dbh2->disconnect;
201
202                 my $db1_moniker = join '', map ucfirst lc, split_name to_identifier $db1;
203                 my $db2_moniker = join '', map ucfirst lc, split_name to_identifier $db2;
204
205                 foreach my $db_schema ({ $db1 => '%', $db2 => '%' }, { '%' => '%' }) {
206                     lives_and {
207                         my @warns;
208                         local $SIG{__WARN__} = sub {
209                             push @warns, $_[0] unless $_[0] =~ /\bcollides\b/
210                                 || $_[0] =~ /unreferencable/;
211                         };
212      
213                         make_schema_at(
214                             'InformixMultiDatabase',
215                             {
216                                 naming => 'current',
217                                 db_schema => $db_schema,
218                                 dump_directory => EXTRA_DUMP_DIR,
219                                 quiet => 1,
220                             },
221                             [ $dsn, $user, $password ],
222                         );
223
224                         InformixMultiDatabase->storage->disconnect;
225
226                         diag join "\n", @warns if @warns;
227
228                         is @warns, 0;
229                     } "dumped schema for databases $db1 and $db2 with no warnings";
230
231                     my $test_schema;
232
233                     lives_and {
234                         ok $test_schema = InformixMultiDatabase->connect($dsn, $user, $password);
235                     } 'connected test schema';
236
237                     my ($rsrc, $rs, $row, $rel_info, %uniqs);
238
239                     lives_and {
240                         ok $rsrc = $test_schema->source("InformixLoaderTest4");
241                     } 'got source for table in database one';
242
243                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
244                         'column in database one';
245
246                     is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
247                         'column in database one';
248
249                     is try { $rsrc->column_info('value')->{size} }, 100,
250                         'column in database one';
251
252                     lives_and {
253                         ok $rs = $test_schema->resultset("InformixLoaderTest4");
254                     } 'got resultset for table in database one';
255
256                     lives_and {
257                         ok $row = $rs->create({ value => 'foo' });
258                     } 'executed SQL on table in database one';
259
260                     $rel_info = try { $rsrc->relationship_info("informix_loader_test5") };
261
262                     is_deeply $rel_info->{cond}, {
263                         'foreign.four_id' => 'self.id'
264                     }, 'relationship in database one';
265
266                     is $rel_info->{attrs}{accessor}, 'single',
267                         'relationship in database one';
268
269                     is $rel_info->{attrs}{join_type}, 'LEFT',
270                         'relationship in database one';
271
272                     lives_and {
273                         ok $rsrc = $test_schema->source("${db1_moniker}InformixLoaderTest5");
274                     } 'got source for table in database one';
275
276                     %uniqs = try { $rsrc->unique_constraints };
277
278                     is keys %uniqs, 2,
279                         'got unique and primary constraint in database one';
280
281                     delete $uniqs{primary};
282
283                     is_deeply ((values %uniqs)[0], ['four_id'],
284                         'correct unique constraint in database one');
285
286                     lives_and {
287                         ok $rsrc = $test_schema->source("InformixLoaderTest6");
288                     } 'got source for table in database two';
289
290                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
291                         'column in database two introspected correctly';
292
293                     is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
294                         'column in database two introspected correctly';
295
296                     is try { $rsrc->column_info('value')->{size} }, 100,
297                         'column in database two introspected correctly';
298
299                     lives_and {
300                         ok $rs = $test_schema->resultset("InformixLoaderTest6");
301                     } 'got resultset for table in database two';
302
303                     lives_and {
304                         ok $row = $rs->create({ value => 'foo' });
305                     } 'executed SQL on table in database two';
306
307                     $rel_info = try { $rsrc->relationship_info('informix_loader_test7') };
308
309                     is_deeply $rel_info->{cond}, {
310                         'foreign.six_id' => 'self.id'
311                     }, 'relationship in database two';
312
313                     is $rel_info->{attrs}{accessor}, 'single',
314                         'relationship in database two';
315
316                     is $rel_info->{attrs}{join_type}, 'LEFT',
317                         'relationship in database two';
318
319                     lives_and {
320                         ok $rsrc = $test_schema->source("InformixLoaderTest7");
321                     } 'got source for table in database two';
322
323                     %uniqs = try { $rsrc->unique_constraints };
324
325                     is keys %uniqs, 2,
326                         'got unique and primary constraint in database two';
327
328                     delete $uniqs{primary};
329
330                     is_deeply ((values %uniqs)[0], ['six_id'],
331                         'correct unique constraint in database two');
332                 }
333             }
334         },
335     },
336 )->run_tests();
337
338 sub db_name {
339     my $schema = shift;
340
341     # When we clone the schema, it still references the original loader, which
342     # references the original schema.
343     local $schema->loader->{schema} = $schema;
344
345     return $schema->loader->_current_db;
346
347     $schema->storage->disconnect;
348 }
349
350 END {
351     if (not $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP}) {
352         if (my $dbh2 = try { $extra_schema->storage->dbh }) {
353
354             try {
355                 $dbh2->do('DROP TABLE informix_loader_test7');
356                 $dbh2->do('DROP TABLE informix_loader_test6');
357                 $dbh2->do('DROP TABLE informix_loader_test5');
358             }
359             catch {
360                 die "Error dropping test tables: $_";
361             };
362
363             $dbh2->disconnect;
364         }
365
366         if (my $dbh1 = try { $schema->storage->dbh }) {
367             
368             try {
369                 $dbh1->do('DROP TABLE informix_loader_test5');
370                 $dbh1->do('DROP TABLE informix_loader_test4');
371             }
372             catch {
373                 die "Error dropping test tables: $_";
374             };
375
376             $dbh1->disconnect;
377         }
378
379         rmtree EXTRA_DUMP_DIR;
380     }
381 }
382 # vim:et sts=4 sw=4 tw=0: