Skip tests segfaulting with ancient DBD::Sybase versions
[dbsrgits/DBIx-Class.git] / t / 74mssql.t
1 use strict;
2 use warnings;  
3
4 # use this if you keep a copy of DBD::Sybase linked to FreeTDS somewhere else
5 BEGIN {
6   if (my $lib_dirs = $ENV{DBICTEST_MSSQL_PERL5LIB}) {
7     unshift @INC, $_ for split /:/, $lib_dirs;
8   }
9 }
10
11 use Test::More;
12 use Test::Exception;
13 use lib qw(t/lib);
14 use DBICTest;
15
16 my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MSSQL_${_}" } qw/DSN USER PASS/};
17
18 plan skip_all => 'Set $ENV{DBICTEST_MSSQL_DSN}, _USER and _PASS to run this test'
19   unless ($dsn);
20
21 my @storage_types = (
22   'DBI::Sybase::Microsoft_SQL_Server',
23   'DBI::Sybase::Microsoft_SQL_Server::NoBindVars',
24 );
25 my $storage_idx = -1;
26 my $schema;
27
28 my $NUMBER_OF_TESTS_IN_BLOCK = 18;
29 for my $storage_type (@storage_types) {
30   $storage_idx++;
31
32   $schema = DBICTest::Schema->clone;
33
34   $schema->connection($dsn, $user, $pass);
35
36   if ($storage_idx != 0) { # autodetect
37     no warnings 'redefine';
38     local *DBIx::Class::Storage::DBI::_typeless_placeholders_supported =
39       sub { 0 };
40 #    $schema->storage_type("::$storage_type");
41     $schema->storage->ensure_connected;
42   }
43   else {
44     $schema->storage->ensure_connected;
45   }
46
47   if ($storage_idx == 0 && ref($schema->storage) =~ /NoBindVars\z/) {
48     my $tb = Test::More->builder;
49     $tb->skip('no placeholders') for 1..$NUMBER_OF_TESTS_IN_BLOCK;
50     next;
51   }
52
53   isa_ok($schema->storage, "DBIx::Class::Storage::$storage_type");
54
55   SKIP: {
56     skip 'This version of DBD::Sybase segfaults on disconnect', 1 if DBD::Sybase->VERSION < 1.08;
57
58     # start disconnected to test _ping
59     $schema->storage->_dbh->disconnect;
60
61     lives_ok {
62       $schema->storage->dbh_do(sub { $_[1]->do('select 1') })
63     } '_ping works';
64   }
65
66   my $dbh = $schema->storage->dbh;
67
68   $dbh->do("IF OBJECT_ID('artist', 'U') IS NOT NULL
69       DROP TABLE artist");
70   $dbh->do("IF OBJECT_ID('cd', 'U') IS NOT NULL
71       DROP TABLE cd");
72
73   $dbh->do("CREATE TABLE artist (artistid INT IDENTITY PRIMARY KEY, name VARCHAR(100), rank INT DEFAULT '13', charfield CHAR(10) NULL);");
74   $dbh->do("CREATE TABLE cd (cdid INT IDENTITY PRIMARY KEY, artist INT,  title VARCHAR(100), year VARCHAR(100), genreid INT NULL, single_track INT NULL);");
75 # Just to test compat shim, Auto is in Core
76   $schema->class('Artist')->load_components('PK::Auto::MSSQL');
77
78 # Test PK
79   my $new = $schema->resultset('Artist')->create( { name => 'foo' } );
80   ok($new->artistid, "Auto-PK worked");
81
82 # Test LIMIT
83   for (1..6) {
84       $schema->resultset('Artist')->create( { name => 'Artist ' . $_, rank => $_ } );
85   }
86
87   my $it = $schema->resultset('Artist')->search( { },
88       { rows     => 3,
89         offset   => 2,
90         order_by => 'artistid'
91       }
92   );
93
94 # Test ? in data don't get treated as placeholders
95   my $cd = $schema->resultset('CD')->create( {
96       artist      => 1,
97       title       => 'Does this break things?',
98       year        => 2007,
99   } );
100   ok($cd->id, 'Not treating ? in data as placeholders');
101
102   is( $it->count, 3, "LIMIT count ok" );
103   ok( $it->next->name, "iterator->next ok" );
104   $it->next;
105   $it->next;
106   is( $it->next, undef, "next past end of resultset ok" );
107
108 # test MONEY column support
109   $schema->storage->dbh_do (sub {
110       my ($storage, $dbh) = @_;
111       eval { $dbh->do("DROP TABLE money_test") };
112       $dbh->do(<<'SQL');
113   CREATE TABLE money_test (
114      id INT IDENTITY PRIMARY KEY,
115      amount MONEY NULL
116   )
117 SQL
118
119   });
120
121   my $rs = $schema->resultset('Money');
122
123   my $row;
124   lives_ok {
125     $row = $rs->create({ amount => 100 });
126   } 'inserted a money value';
127
128   cmp_ok $rs->find($row->id)->amount, '==', 100, 'money value round-trip';
129
130   lives_ok {
131     $row->update({ amount => 200 });
132   } 'updated a money value';
133
134   cmp_ok $rs->find($row->id)->amount, '==', 200,
135     'updated money value round-trip';
136
137   lives_ok {
138     $row->update({ amount => undef });
139   } 'updated a money value to NULL';
140
141   is $rs->find($row->id)->amount,
142     undef, 'updated money value to NULL round-trip';
143
144   $rs->create({ amount => 300 }) for (1..3);
145
146   # test multiple active statements
147   lives_ok {
148     my $artist_rs = $schema->resultset('Artist');
149     while (my $row = $rs->next) {
150       my $artist = $artist_rs->next;
151     }
152     $rs->reset;
153   } 'multiple active statements';
154
155   $rs->delete;
156
157   # test simple transaction with commit
158   lives_ok {
159     $schema->txn_do(sub {
160       $rs->create({ amount => 400 });
161     });
162   } 'simple transaction';
163
164   cmp_ok $rs->first->amount, '==', 400, 'committed';
165   $rs->reset;
166
167   $rs->delete;
168
169   # test rollback
170   throws_ok {
171     $schema->txn_do(sub {
172       $rs->create({ amount => 400 });
173       die 'mtfnpy';
174     });
175   } qr/mtfnpy/, 'simple failed txn';
176
177   is $rs->first, undef, 'rolled back';
178   $rs->reset;
179
180   # test RNO detection when version detection fails
181   SKIP: {
182     my $storage = $schema->storage;
183     my $version = $storage->_server_info->{normalized_dbms_version};
184
185     skip 'could not detect SQL Server version', 1 if not defined $version;
186
187     my $have_rno = $version >= 9 ? 1 : 0;
188
189     # Delete version information to force RNO check when rebuilding SQLA
190     # instance.
191     no strict 'refs';
192     no warnings 'redefine';
193     local *{(ref $storage).'::_get_server_version'} = sub { undef };
194
195     my $server_info = { %{ $storage->_server_info_hash } }; # clone
196
197     delete @$server_info{qw/dbms_version normalized_dbms_version/};
198
199     local $storage->{_server_info_hash} = $server_info;
200     local $storage->{_sql_maker}        = undef;
201     local $storage->{_sql_maker_opts}   = undef;
202
203     $storage->sql_maker;
204
205     my $rno_detected =
206       ($storage->{_sql_maker_opts}{limit_dialect} eq 'RowNumberOver') ? 1 : 0;
207
208     ok (($have_rno == $rno_detected),
209       'row_number() over support detected correctly');
210   }
211
212   {
213     my $schema = DBICTest::Schema->clone;
214     $schema->connection($dsn, $user, $pass);
215
216     like $schema->storage->sql_maker->{limit_dialect},
217       qr/^(?:Top|RowNumberOver)\z/,
218       'sql_maker is correct on unconnected schema';
219   }
220 }
221
222 # test op-induced autoconnect
223 lives_ok (sub {
224
225   my $schema =  DBICTest::Schema->clone;
226   $schema->connection($dsn, $user, $pass);
227
228   my $artist = $schema->resultset ('Artist')->search ({}, { order_by => 'artistid' })->next;
229   is ($artist->id, 1, 'Artist retrieved successfully');
230 }, 'Query-induced autoconnect works');
231
232 done_testing;
233
234 # clean up our mess
235 END {
236   if (my $dbh = eval { $schema->storage->dbh }) {
237     $dbh->do("IF OBJECT_ID('artist', 'U') IS NOT NULL DROP TABLE artist");
238     $dbh->do("IF OBJECT_ID('cd', 'U') IS NOT NULL DROP TABLE cd");
239     $dbh->do("IF OBJECT_ID('money_test', 'U') IS NOT NULL DROP TABLE money_test");
240   }
241 }