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