3397f3c843f2111d53a16aae8f470a8f64e485f3
[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 $testdb_supports_placeholders = DBICTest::Schema->connect($dsn, $user, $pass)
22                                                     ->storage
23                                                      ->_supports_typeless_placeholders;
24 my @test_storages = (
25   $testdb_supports_placeholders ? 'DBI::Sybase::Microsoft_SQL_Server' : (),
26   'DBI::Sybase::Microsoft_SQL_Server::NoBindVars',
27 );
28
29 my $schema;
30 for my $storage_type (@test_storages) {
31   $schema = DBICTest::Schema->connect($dsn, $user, $pass);
32
33   if ($storage_type =~ /NoBindVars\z/) {
34     # since we want to use the nobindvar - disable the capability so the
35     # rebless happens to the correct class
36     $schema->storage->_use_typeless_placeholders (0);
37   }
38
39   $schema->storage->ensure_connected;
40   isa_ok($schema->storage, "DBIx::Class::Storage::$storage_type");
41
42   SKIP: {
43     skip 'This version of DBD::Sybase segfaults on disconnect', 1 if DBD::Sybase->VERSION < 1.08;
44
45     # start disconnected to test _ping
46     $schema->storage->_dbh->disconnect;
47
48     lives_ok {
49       $schema->storage->dbh_do(sub { $_[1]->do('select 1') })
50     } '_ping works';
51   }
52
53   my $dbh = $schema->storage->dbh;
54
55   $dbh->do("IF OBJECT_ID('artist', 'U') IS NOT NULL
56       DROP TABLE artist");
57   $dbh->do("IF OBJECT_ID('cd', 'U') IS NOT NULL
58       DROP TABLE cd");
59
60   $dbh->do("CREATE TABLE artist (artistid INT IDENTITY PRIMARY KEY, name VARCHAR(100), rank INT DEFAULT '13', charfield CHAR(10) NULL);");
61   $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);");
62 # Just to test compat shim, Auto is in Core
63   $schema->class('Artist')->load_components('PK::Auto::MSSQL');
64
65 # Test PK
66   my $new = $schema->resultset('Artist')->create( { name => 'foo' } );
67   ok($new->artistid, "Auto-PK worked");
68
69 # Test LIMIT
70   for (1..6) {
71       $schema->resultset('Artist')->create( { name => 'Artist ' . $_, rank => $_ } );
72   }
73
74   my $it = $schema->resultset('Artist')->search( { },
75       { rows     => 3,
76         offset   => 2,
77         order_by => 'artistid'
78       }
79   );
80
81 # Test ? in data don't get treated as placeholders
82   my $cd = $schema->resultset('CD')->create( {
83       artist      => 1,
84       title       => 'Does this break things?',
85       year        => 2007,
86   } );
87   ok($cd->id, 'Not treating ? in data as placeholders');
88
89   is( $it->count, 3, "LIMIT count ok" );
90   ok( $it->next->name, "iterator->next ok" );
91   $it->next;
92   $it->next;
93   is( $it->next, undef, "next past end of resultset ok" );
94
95 # test MONEY column support
96   $schema->storage->dbh_do (sub {
97       my ($storage, $dbh) = @_;
98       eval { $dbh->do("DROP TABLE money_test") };
99       $dbh->do(<<'SQL');
100   CREATE TABLE money_test (
101      id INT IDENTITY PRIMARY KEY,
102      amount MONEY NULL
103   )
104 SQL
105
106   });
107
108   my $rs = $schema->resultset('Money');
109
110   my $row;
111   lives_ok {
112     $row = $rs->create({ amount => 100 });
113   } 'inserted a money value';
114
115   cmp_ok $rs->find($row->id)->amount, '==', 100, 'money value round-trip';
116
117   lives_ok {
118     $row->update({ amount => 200 });
119   } 'updated a money value';
120
121   cmp_ok $rs->find($row->id)->amount, '==', 200,
122     'updated money value round-trip';
123
124   lives_ok {
125     $row->update({ amount => undef });
126   } 'updated a money value to NULL';
127
128   is $rs->find($row->id)->amount,
129     undef, 'updated money value to NULL round-trip';
130
131   $rs->create({ amount => 300 }) for (1..3);
132
133   # test multiple active statements
134   lives_ok {
135     my $artist_rs = $schema->resultset('Artist');
136     while (my $row = $rs->next) {
137       my $artist = $artist_rs->next;
138     }
139     $rs->reset;
140   } 'multiple active statements';
141
142   $rs->delete;
143
144   # test simple transaction with commit
145   lives_ok {
146     $schema->txn_do(sub {
147       $rs->create({ amount => 400 });
148     });
149   } 'simple transaction';
150
151   cmp_ok $rs->first->amount, '==', 400, 'committed';
152   $rs->reset;
153
154   $rs->delete;
155
156   # test rollback
157   throws_ok {
158     $schema->txn_do(sub {
159       $rs->create({ amount => 400 });
160       die 'mtfnpy';
161     });
162   } qr/mtfnpy/, 'simple failed txn';
163
164   is $rs->first, undef, 'rolled back';
165   $rs->reset;
166
167   # test RNO detection when version detection fails
168   SKIP: {
169     my $storage = $schema->storage;
170     my $version = $storage->_server_info->{normalized_dbms_version};
171
172     skip 'could not detect SQL Server version', 1 if not defined $version;
173
174     my $have_rno = $version >= 9 ? 1 : 0;
175
176     local $storage->{_sql_maker}        = undef;
177     local $storage->{_sql_maker_opts}   = undef;
178
179     local $storage->{_dbh_details}{info} = {}; # delete cache
180
181     $storage->sql_maker;
182
183     my $rno_detected =
184       ($storage->{_sql_maker_opts}{limit_dialect} eq 'RowNumberOver') ? 1 : 0;
185
186     ok (($have_rno == $rno_detected),
187       'row_number() over support detected correctly');
188   }
189
190   {
191     my $schema = DBICTest::Schema->clone;
192     $schema->connection($dsn, $user, $pass);
193
194     like $schema->storage->sql_maker->{limit_dialect},
195       qr/^(?:Top|RowNumberOver)\z/,
196       'sql_maker is correct on unconnected schema';
197   }
198 }
199
200 # test op-induced autoconnect
201 lives_ok (sub {
202
203   my $schema =  DBICTest::Schema->clone;
204   $schema->connection($dsn, $user, $pass);
205
206   my $artist = $schema->resultset ('Artist')->search ({}, { order_by => 'artistid' })->next;
207   is ($artist->id, 1, 'Artist retrieved successfully');
208 }, 'Query-induced autoconnect works');
209
210 done_testing;
211
212 # clean up our mess
213 END {
214   if (my $dbh = eval { $schema->storage->dbh }) {
215     $dbh->do("IF OBJECT_ID('artist', 'U') IS NOT NULL DROP TABLE artist");
216     $dbh->do("IF OBJECT_ID('cd', 'U') IS NOT NULL DROP TABLE cd");
217     $dbh->do("IF OBJECT_ID('money_test', 'U') IS NOT NULL DROP TABLE money_test");
218   }
219 }