afce0d090fa27fa9aa1c31588446819f1e1cc473
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / 10_06sybase_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 'make_schema_at';
8 use namespace::clean;
9 use DBI ();
10
11 use lib qw(t/lib);
12
13 use dbixcsl_common_tests ();
14 use dbixcsl_test_dir '$tdir';
15
16 use constant EXTRA_DUMP_DIR => "$tdir/sybase_extra_dump";
17
18 my $dsn      = $ENV{DBICTEST_SYBASE_DSN} || '';
19 my $user     = $ENV{DBICTEST_SYBASE_USER} || '';
20 my $password = $ENV{DBICTEST_SYBASE_PASS} || '';
21
22 BEGIN { $ENV{DBIC_SYBASE_FREETDS_NOWARN} = 1 }
23
24 my ($schema, $databases_created); # for cleanup in END for extra tests
25
26 my $tester = dbixcsl_common_tests->new(
27     vendor      => 'sybase',
28     auto_inc_pk => 'INTEGER IDENTITY NOT NULL PRIMARY KEY',
29     default_function     => 'getdate()',
30     default_function_def => 'AS getdate()',
31     dsn         => $dsn,
32     user        => $user,
33     password    => $password,
34     data_types  => {
35         # http://ispirer.com/wiki/sqlways/sybase/data-types
36         #
37         # Numeric Types
38         'integer identity' => { data_type => 'integer', is_auto_increment => 1 },
39         int      => { data_type => 'integer' },
40         integer  => { data_type => 'integer' },
41         bigint   => { data_type => 'bigint' },
42         smallint => { data_type => 'smallint' },
43         tinyint  => { data_type => 'tinyint' },
44         'double precision' => { data_type => 'double precision' },
45         real           => { data_type => 'real' },
46         float          => { data_type => 'double precision' },
47         'float(14)'    => { data_type => 'real' },
48         'float(15)'    => { data_type => 'real' },
49         'float(16)'    => { data_type => 'double precision' },
50         'float(48)'    => { data_type => 'double precision' },
51         'numeric(6,3)' => { data_type => 'numeric', size => [6,3] },
52         'decimal(6,3)' => { data_type => 'numeric', size => [6,3] },
53         numeric        => { data_type => 'numeric' },
54         decimal        => { data_type => 'numeric' },
55         bit            => { data_type => 'bit' },
56
57         # Money Types
58         money          => { data_type => 'money' },
59         smallmoney     => { data_type => 'smallmoney' },
60
61         # Computed Column
62         'AS getdate()'     => { data_type => undef, inflate_datetime => 1, default_value => \'getdate()' },
63
64         # Blob Types
65         text     => { data_type => 'text' },
66         unitext  => { data_type => 'unitext' },
67         image    => { data_type => 'image' },
68
69         # DateTime Types
70         date     => { data_type => 'date' },
71         time     => { data_type => 'time' },
72         datetime => { data_type => 'datetime' },
73         smalldatetime  => { data_type => 'smalldatetime' },
74
75         # Timestamp column
76         timestamp      => { data_type => 'timestamp', inflate_datetime => 0 },
77
78         # String Types
79         'char'         => { data_type => 'char', size => 1 },
80         'char(2)'      => { data_type => 'char', size => 2 },
81         'nchar'        => { data_type => 'nchar', size => 1 },
82         'nchar(2)'     => { data_type => 'nchar', size => 2 },
83         'unichar(2)'   => { data_type => 'unichar', size => 2 },
84         'varchar(2)'   => { data_type => 'varchar', size => 2 },
85         'nvarchar(2)'  => { data_type => 'nvarchar', size => 2 },
86         'univarchar(2)' => { data_type => 'univarchar', size => 2 },
87
88         # Binary Types
89         'binary'       => { data_type => 'binary', size => 1 },
90         'binary(2)'    => { data_type => 'binary', size => 2 },
91         'varbinary(2)' => { data_type => 'varbinary', size => 2 },
92     },
93     # test that named constraints aren't picked up as tables (I can't reproduce this on my machine)
94     failtrigger_warnings => [ qr/^Bad table or view 'sybase_loader_test2_ref_slt1'/ ],
95     extra => {
96         create => [
97             q{
98                 CREATE TABLE sybase_loader_test1 (
99                     id int identity primary key
100                 )
101             },
102             q{
103                 CREATE TABLE sybase_loader_test2 (
104                     id int identity primary key,
105                     sybase_loader_test1_id int,
106                     CONSTRAINT sybase_loader_test2_ref_slt1 FOREIGN KEY (sybase_loader_test1_id) REFERENCES sybase_loader_test1 (id)
107                 )
108             },
109         ],
110         drop => [ qw/sybase_loader_test1 sybase_loader_test2/ ],
111         count => 30 * 4,
112         run => sub {
113             $schema = shift;
114
115             SKIP: {
116                 my $dbh = $schema->storage->dbh;
117
118                 try {
119                     $dbh->do('USE master');
120                 }
121                 catch {
122                     skip "these tests require the sysadmin role", 30 * 4;
123                 };
124
125                 try {
126                     $dbh->do('CREATE DATABASE [dbicsl_test1]');
127                     $dbh->do('CREATE DATABASE [dbicsl_test2]');
128                 }
129                 catch {
130                     skip "cannot create databases: $_", 30 * 4;
131                 };
132
133                 try {
134                     my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
135                     local $SIG{__WARN__} = sub {
136                         $warn_handler->(@_)
137                             unless $_[0] =~ /^Password correctly set\.$|^Account unlocked\.$|^New login created\.$|^New user added\.$/;
138                     };
139
140                     $dbh->do("sp_addlogin dbicsl_user1, dbicsl, [dbicsl_test1]");
141                     $dbh->do("sp_addlogin dbicsl_user2, dbicsl, [dbicsl_test2]");
142
143                     $dbh->do("USE [dbicsl_test1]");
144                     $dbh->do("sp_adduser dbicsl_user1");
145                     $dbh->do("sp_adduser dbicsl_user2");
146                     $dbh->do("GRANT ALL TO dbicsl_user1");
147                     $dbh->do("GRANT ALL TO dbicsl_user2");
148
149                     $dbh->do("USE [dbicsl_test2]");
150                     $dbh->do("sp_adduser dbicsl_user2");
151                     $dbh->do("sp_adduser dbicsl_user1");
152                     $dbh->do("GRANT ALL TO dbicsl_user2");
153                     $dbh->do("GRANT ALL TO dbicsl_user1");
154                 }
155                 catch {
156                     skip "cannot add logins: $_", 30 * 4;
157                 };
158
159                 my ($dbh1, $dbh2);
160                 {
161                     my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
162                     local $SIG{__WARN__} = sub {
163                         $warn_handler->(@_) unless $_[0] =~ /can't change context/;
164                     };
165
166                     $dbh1 = DBI->connect($dsn, 'dbicsl_user1', 'dbicsl', {
167                         RaiseError => 1,
168                         PrintError => 0,
169                     });
170                     $dbh1->do('USE [dbicsl_test1]');
171
172                     $dbh2 = DBI->connect($dsn, 'dbicsl_user2', 'dbicsl', {
173                         RaiseError => 1,
174                         PrintError => 0,
175                     });
176                     $dbh2->do('USE [dbicsl_test2]');
177                 }
178
179                 $dbh1->do(<<"EOF");
180                     CREATE TABLE sybase_loader_test4 (
181                         id INT IDENTITY PRIMARY KEY,
182                         value VARCHAR(100) NULL
183                     )
184 EOF
185                 $dbh1->do('GRANT ALL ON sybase_loader_test4 TO dbicsl_user2');
186                 $dbh1->do(<<"EOF");
187                     CREATE TABLE sybase_loader_test5 (
188                         id INT IDENTITY PRIMARY KEY,
189                         value VARCHAR(100) NULL,
190                         four_id INTEGER,
191                         CONSTRAINT loader_test5_uniq UNIQUE (four_id),
192                         FOREIGN KEY (four_id) REFERENCES sybase_loader_test4 (id)
193                     )
194 EOF
195                 $dbh2->do(<<"EOF");
196                     CREATE TABLE sybase_loader_test5 (
197                         pk INT IDENTITY PRIMARY KEY,
198                         value VARCHAR(100) NULL,
199                         four_id INTEGER,
200                         CONSTRAINT loader_test5_uniq UNIQUE (four_id),
201                         FOREIGN KEY (four_id) REFERENCES [dbicsl_test1].dbicsl_user1.sybase_loader_test4 (id)
202                     )
203 EOF
204                 $dbh2->do(<<"EOF");
205                     CREATE TABLE sybase_loader_test6 (
206                         id INT IDENTITY PRIMARY KEY,
207                         value VARCHAR(100) NULL,
208                         sybase_loader_test4_id INTEGER NULL,
209                         FOREIGN KEY (sybase_loader_test4_id) REFERENCES [dbicsl_test1].dbicsl_user1.sybase_loader_test4 (id)
210                     )
211 EOF
212                 $dbh2->do(<<"EOF");
213                     CREATE TABLE sybase_loader_test7 (
214                         id INT IDENTITY PRIMARY KEY,
215                         value VARCHAR(100) NULL,
216                         six_id INTEGER UNIQUE,
217                         FOREIGN KEY (six_id) REFERENCES sybase_loader_test6 (id)
218                     )
219 EOF
220                 $dbh2->do('GRANT ALL ON sybase_loader_test7 TO dbicsl_user1');
221                 $dbh1->do(<<"EOF");
222                     CREATE TABLE sybase_loader_test8 (
223                         id INT IDENTITY PRIMARY KEY,
224                         value VARCHAR(100) NULL,
225                         sybase_loader_test7_id INTEGER,
226                         FOREIGN KEY (sybase_loader_test7_id) REFERENCES [dbicsl_test2].dbicsl_user2.sybase_loader_test7 (id)
227                     )
228 EOF
229
230                 $databases_created = 1;
231
232                 foreach my $databases (['dbicsl_test1', 'dbicsl_test2'], '%') {
233                     foreach my $owners ([qw/dbicsl_user1 dbicsl_user2/], '%') {
234                         lives_and {
235                             rmtree EXTRA_DUMP_DIR;
236
237                             my @warns;
238                             local $SIG{__WARN__} = sub {
239                                 push @warns, $_[0] unless $_[0] =~ /\bcollides\b/
240                                     || $_[0] =~ /can't change context/;
241                             };
242
243                             my $database = $databases;
244
245                             $database = [ $database ] unless ref $database;
246
247                             my $db_schema = {};
248
249                             foreach my $db (@$database) {
250                                 $db_schema->{$db} = $owners;
251                             }
252
253                             make_schema_at(
254                                 'SybaseMultiSchema',
255                                 {
256                                     naming => 'current',
257                                     db_schema => $db_schema,
258                                     dump_directory => EXTRA_DUMP_DIR,
259                                     quiet => 1,
260                                 },
261                                 [ $dsn, $user, $password ],
262                             );
263
264                             diag join "\n", @warns if @warns;
265
266                             is @warns, 0;
267                         } 'dumped schema for "dbicsl_test1" and "dbicsl_test2" databases with no warnings';
268
269                         my ($test_schema, $rsrc, $rs, $row, %uniqs, $rel_info);
270
271                         lives_and {
272                             ok $test_schema = SybaseMultiSchema->connect($dsn, $user, $password);
273                         } 'connected test schema';
274
275                         lives_and {
276                             ok $rsrc = $test_schema->source('SybaseLoaderTest4');
277                         } 'got source for table in database one';
278
279                         is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
280                             'column in database one';
281
282                         is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
283                             'column in database one';
284
285                         is try { $rsrc->column_info('value')->{size} }, 100,
286                             'column in database one';
287
288                         lives_and {
289                             ok $rs = $test_schema->resultset('SybaseLoaderTest4');
290                         } 'got resultset for table in database one';
291
292                         lives_and {
293                             ok $row = $rs->create({ value => 'foo' });
294                         } 'executed SQL on table in database one';
295
296                         $rel_info = try { $rsrc->relationship_info('dbicsl_test1_sybase_loader_test5') };
297
298                         is_deeply $rel_info->{cond}, {
299                             'foreign.four_id' => 'self.id'
300                         }, 'relationship in database one';
301
302                         is $rel_info->{attrs}{accessor}, 'single',
303                             'relationship in database one';
304
305                         is $rel_info->{attrs}{join_type}, 'LEFT',
306                             'relationship in database one';
307
308                         lives_and {
309                             ok $rsrc = $test_schema->source('DbicslTest1SybaseLoaderTest5');
310                         } 'got source for table in database one';
311
312                         %uniqs = try { $rsrc->unique_constraints };
313
314                         is keys %uniqs, 2,
315                             'got unique and primary constraint in database one';
316
317                         delete $uniqs{primary};
318
319                         is_deeply ((values %uniqs)[0], ['four_id'],
320                             'correct unique constraint in database one');
321
322                         lives_and {
323                             ok $rsrc = $test_schema->source('SybaseLoaderTest6');
324                         } 'got source for table in database two';
325
326                         is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
327                             'column in database two introspected correctly';
328
329                         is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
330                             'column in database two introspected correctly';
331
332                         is try { $rsrc->column_info('value')->{size} }, 100,
333                             'column in database two introspected correctly';
334
335                         lives_and {
336                             ok $rs = $test_schema->resultset('SybaseLoaderTest6');
337                         } 'got resultset for table in database two';
338
339                         lives_and {
340                             ok $row = $rs->create({ value => 'foo' });
341                         } 'executed SQL on table in database two';
342
343                         $rel_info = try { $rsrc->relationship_info('sybase_loader_test7') };
344
345                         is_deeply $rel_info->{cond}, {
346                             'foreign.six_id' => 'self.id'
347                         }, 'relationship in database two';
348
349                         is $rel_info->{attrs}{accessor}, 'single',
350                             'relationship in database two';
351
352                         is $rel_info->{attrs}{join_type}, 'LEFT',
353                             'relationship in database two';
354
355                         lives_and {
356                             ok $rsrc = $test_schema->source('SybaseLoaderTest7');
357                         } 'got source for table in database two';
358
359                         %uniqs = try { $rsrc->unique_constraints };
360
361                         is keys %uniqs, 2,
362                             'got unique and primary constraint in database two';
363
364                         delete $uniqs{primary};
365
366                         is_deeply ((values %uniqs)[0], ['six_id'],
367                             'correct unique constraint in database two');
368
369                         lives_and {
370                             ok $test_schema->source('SybaseLoaderTest6')
371                                 ->has_relationship('sybase_loader_test4');
372                         } 'cross-database relationship in multi database schema';
373
374                         lives_and {
375                             ok $test_schema->source('SybaseLoaderTest4')
376                                 ->has_relationship('sybase_loader_test6s');
377                         } 'cross-database relationship in multi database schema';
378
379                         lives_and {
380                             ok $test_schema->source('SybaseLoaderTest8')
381                                 ->has_relationship('sybase_loader_test7');
382                         } 'cross-database relationship in multi database schema';
383
384                         lives_and {
385                             ok $test_schema->source('SybaseLoaderTest7')
386                                 ->has_relationship('sybase_loader_test8s');
387                         } 'cross-database relationship in multi database schema';
388                     }
389                 }
390             }
391         },
392     },
393 );
394
395 if( !$dsn || !$user ) {
396     $tester->skip_tests('You need to set the DBICTEST_SYBASE_DSN, _USER, and _PASS environment variables');
397 }
398 else {
399     $tester->run_tests();
400 }
401
402 END {
403     if (not $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP}) {
404         rmtree EXTRA_DUMP_DIR;
405
406         if ($databases_created) {
407             my $dbh = $schema->storage->dbh;
408
409             $dbh->do('USE master');
410
411             local $dbh->{FetchHashKeyName} = 'NAME_lc';
412
413             my $sth = $dbh->prepare('sp_who');
414             $sth->execute;
415
416             while (my $row = $sth->fetchrow_hashref) {
417                 if ($row->{dbname} =~ /^dbicsl_test[12]\z/) {
418                     $dbh->do("kill $row->{spid}");
419                 }
420             }
421
422             foreach my $table ('[dbicsl_test1].dbicsl_user1.sybase_loader_test8',
423                                '[dbicsl_test2].dbicsl_user2.sybase_loader_test7',
424                                '[dbicsl_test2].dbicsl_user2.sybase_loader_test6',
425                                '[dbicsl_test2].dbicsl_user2.sybase_loader_test5',
426                                '[dbicsl_test1].dbicsl_user1.sybase_loader_test5',
427                                '[dbicsl_test1].dbicsl_user1.sybase_loader_test4') {
428                 try {
429                     $dbh->do("DROP TABLE $table");
430                 }
431                 catch {
432                     diag "Error dropping table $table: $_";
433                 };
434             }
435
436             foreach my $db (qw/dbicsl_test1 dbicsl_test2/) {
437                 try {
438                     $dbh->do("DROP DATABASE [$db]");
439                 }
440                 catch {
441                     diag "Error dropping test database $db: $_";
442                 };
443             }
444
445             foreach my $login (qw/dbicsl_user1 dbicsl_user2/) {
446                 try {
447                     my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
448                     local $SIG{__WARN__} = sub {
449                         $warn_handler->(@_)
450                             unless $_[0] =~ /^Account locked\.$|^Login dropped\.$/;
451                     };
452
453                     $dbh->do("sp_droplogin $login");
454                 }
455                 catch {
456                     diag "Error dropping login $login: $_"
457                         unless /Incorrect syntax/;
458                 };
459             }
460         }
461     }
462 }
463 # vim:et sts=4 sw=4 tw=0: