Fix updating multiple CLOB/BLOB columns on Oracle
[dbsrgits/DBIx-Class.git] / t / 747mssql_ado.t
CommitLineData
4ffa5700 1use strict;
2use warnings;
3
4use Test::More;
2edf3352 5use Test::Exception;
6use Try::Tiny;
199fbc45 7use DBIx::Class::Optional::Dependencies ();
4ffa5700 8use lib qw(t/lib);
9use DBICTest;
10
199fbc45 11plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('test_rdbms_mssql_ado')
12 unless DBIx::Class::Optional::Dependencies->req_ok_for ('test_rdbms_mssql_ado');
13
56dca25f 14# Example DSN (from frew):
15# dbi:ADO:PROVIDER=sqlncli10;SERVER=tcp:172.24.2.10;MARS Connection=True;Initial Catalog=CIS;UID=cis_web;PWD=...;DataTypeCompatibility=80;
16
4ffa5700 17my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MSSQL_ADO_${_}" } qw/DSN USER PASS/};
18
19plan skip_all => 'Set $ENV{DBICTEST_MSSQL_ADO_DSN}, _USER and _PASS to run this test'
20 unless ($dsn && $user);
21
2c2bc4e5 22require DBICTest::Schema;
2edf3352 23DBICTest::Schema->load_classes(qw/VaryingMAX ArtistGUID/);
24
25my %binstr = ( 'small' => join('', map { chr($_) } ( 1 .. 127 )) );
26$binstr{'large'} = $binstr{'small'} x 1024;
27
28my $maxloblen = length $binstr{'large'};
29
30my $schema = DBICTest::Schema->connect($dsn, $user, $pass, {
31 auto_savepoint => 1,
32 LongReadLen => $maxloblen,
33});
34
4ffa5700 35$schema->storage->ensure_connected;
36
2edf3352 37isa_ok($schema->storage, 'DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server');
4ffa5700 38
56dca25f 39my $ver = $schema->storage->_server_info->{normalized_dbms_version};
40
41ok $ver, 'can introspect DBMS version';
42
2edf3352 43# 2005 and greater
56dca25f 44is $schema->storage->sql_limit_dialect, ($ver >= 9 ? 'RowNumberOver' : 'Top'),
45 'correct limit dialect detected';
46
4ffa5700 47$schema->storage->dbh_do (sub {
48 my ($storage, $dbh) = @_;
2edf3352 49 try { local $^W = 0; $dbh->do("DROP TABLE artist") };
4ffa5700 50 $dbh->do(<<'SQL');
51CREATE TABLE artist (
52 artistid INT IDENTITY NOT NULL,
53 name VARCHAR(100),
54 rank INT NOT NULL DEFAULT '13',
55 charfield CHAR(10) NULL,
56 primary key(artistid)
57)
58SQL
59});
60
2edf3352 61$schema->storage->dbh_do (sub {
62 my ($storage, $dbh) = @_;
63 try { local $^W = 0; $dbh->do("DROP TABLE artist_guid") };
64 $dbh->do(<<"SQL");
65CREATE TABLE artist_guid (
66 artistid UNIQUEIDENTIFIER NOT NULL,
67 name VARCHAR(100),
68 rank INT NULL,
69 charfield CHAR(10) NULL,
70 a_guid UNIQUEIDENTIFIER,
71 primary key(artistid)
72)
73SQL
74});
75
76my $have_max = $ver >= 9; # 2005 and greater
77
78$schema->storage->dbh_do (sub {
79 my ($storage, $dbh) = @_;
80 try { local $^W = 0; $dbh->do("DROP TABLE varying_max_test") };
81 $dbh->do("
82CREATE TABLE varying_max_test (
83 id INT IDENTITY NOT NULL,
84" . ($have_max ? "
85 varchar_max VARCHAR(MAX),
86 nvarchar_max NVARCHAR(MAX),
87 varbinary_max VARBINARY(MAX),
88" : "
89 varchar_max TEXT,
90 nvarchar_max NTEXT,
91 varbinary_max IMAGE,
92") . "
93 primary key(id)
94)");
95});
96
97my $ars = $schema->resultset('Artist');
98
99my $new = $ars->create({ name => 'foo' });
4ffa5700 100ok($new->artistid > 0, 'Auto-PK worked');
101
102# make sure select works
103my $found = $schema->resultset('Artist')->search({ name => 'foo' })->first;
104is $found->artistid, $new->artistid, 'search works';
105
7282bf38 106# test large column list in select
107$found = $schema->resultset('Artist')->search({ name => 'foo' }, {
56dca25f 108 select => ['artistid', 'name', map \"'foo' foo_$_", 0..50],
109 as => ['artistid', 'name', map "foo_$_", 0..50],
7282bf38 110})->first;
111is $found->artistid, $new->artistid, 'select with big column list';
112is $found->get_column('foo_50'), 'foo', 'last item in big column list';
e38348dd 113
4ffa5700 114# create a few more rows
8bcd9ece 115for (1..12) {
4ffa5700 116 $schema->resultset('Artist')->create({ name => 'Artist ' . $_ });
117}
118
119# test multiple active cursors
7c5b1b9f 120my $rs1 = $schema->resultset('Artist')->search({}, { order_by => 'artistid' });
121my $rs2 = $schema->resultset('Artist')->search({}, { order_by => 'name' });
4ffa5700 122
123while ($rs1->next) {
2edf3352 124 ok try { $rs2->next }, 'multiple active cursors';
4ffa5700 125}
126
8bcd9ece 127# test bug where ADO blows up if the first bindparam is shorter than the second
128is $schema->resultset('Artist')->search({ artistid => 2 })->first->name,
129 'Artist 1',
130 'short bindparam';
131
132is $schema->resultset('Artist')->search({ artistid => 13 })->first->name,
133 'Artist 12',
134 'longer bindparam';
135
2edf3352 136# test explicit key spec
137$new = $ars->create ({ name => 'bar', artistid => 66 });
138is($new->artistid, 66, 'Explicit PK worked');
139$new->discard_changes;
140is($new->artistid, 66, 'Explicit PK assigned');
141
142# test basic transactions
143$schema->txn_do(sub {
144 $ars->create({ name => 'transaction_commit' });
145});
146ok($ars->search({ name => 'transaction_commit' })->first,
147 'transaction committed');
148$ars->search({ name => 'transaction_commit' })->delete,
149throws_ok {
150 $schema->txn_do(sub {
151 $ars->create({ name => 'transaction_rollback' });
152 die 'rolling back';
153 });
154} qr/rolling back/, 'rollback executed';
155is $ars->search({ name => 'transaction_rollback' })->first, undef,
156 'transaction rolled back';
157
158# test two-phase commit and inner transaction rollback from nested transactions
159$schema->txn_do(sub {
160 $ars->create({ name => 'in_outer_transaction' });
161 $schema->txn_do(sub {
162 $ars->create({ name => 'in_inner_transaction' });
163 });
164 ok($ars->search({ name => 'in_inner_transaction' })->first,
165 'commit from inner transaction visible in outer transaction');
166 throws_ok {
167 $schema->txn_do(sub {
168 $ars->create({ name => 'in_inner_transaction_rolling_back' });
169 die 'rolling back inner transaction';
170 });
171 } qr/rolling back inner transaction/, 'inner transaction rollback executed';
172});
173ok($ars->search({ name => 'in_outer_transaction' })->first,
174 'commit from outer transaction');
175ok($ars->search({ name => 'in_inner_transaction' })->first,
176 'commit from inner transaction');
177is $ars->search({ name => 'in_inner_transaction_rolling_back' })->first,
178 undef,
179 'rollback from inner transaction';
180$ars->search({ name => 'in_outer_transaction' })->delete;
181$ars->search({ name => 'in_inner_transaction' })->delete;
182
183# test populate
184lives_ok (sub {
185 my @pop;
186 for (1..2) {
187 push @pop, { name => "Artist_$_" };
188 }
189 $ars->populate (\@pop);
190});
191
192# test populate with explicit key
193lives_ok (sub {
194 my @pop;
195 for (1..2) {
196 push @pop, { name => "Artist_expkey_$_", artistid => 100 + $_ };
197 }
198 $ars->populate (\@pop);
199});
200
201# count what we did so far
202is ($ars->count, 18, 'Simple count works');
203
204# test empty insert
205my $current_artistid = $ars->search({}, {
206 select => [ { max => 'artistid' } ], as => ['artistid']
207})->first->artistid;
208
209my $row;
210lives_ok { $row = $ars->create({}) }
211 'empty insert works';
212
213$row->discard_changes;
214
215is $row->artistid, $current_artistid+1,
216 'empty insert generated correct PK';
217
218# test that autoinc column still works after empty insert
219 $row = $ars->create({ name => 'after_empty_insert' });
220
221 is $row->artistid, $current_artistid+2,
222 'autoincrement column functional aftear empty insert';
223
224my $rs = $schema->resultset('VaryingMAX');
225
226foreach my $size (qw/small large/) {
49eeb48d 227 local $schema->storage->{debug} = 0 if $size eq 'large';
2edf3352 228
229 my $str = $binstr{$size};
230 my $row;
231 lives_ok {
232 $row = $rs->create({
233 varchar_max => $str, nvarchar_max => $str, varbinary_max => $str
234 });
235 } "created $size VARXXX(MAX) LOBs";
236
237 lives_ok {
238 $row->discard_changes;
239 } 're-selected just-inserted LOBs';
240
241 cmp_ok try { $row->varchar_max }, 'eq', $str, 'VARCHAR(MAX) matches';
242 cmp_ok try { $row->nvarchar_max }, 'eq', $str, 'NVARCHAR(MAX) matches';
243 cmp_ok try { $row->varbinary_max }, 'eq', $str, 'VARBINARY(MAX) matches';
2edf3352 244}
245
246# test regular blobs
247
248try { local $^W = 0; $schema->storage->dbh->do('DROP TABLE bindtype_test') };
249$schema->storage->dbh->do(qq[
250CREATE TABLE bindtype_test
251(
252 id INT IDENTITY NOT NULL PRIMARY KEY,
253 bytea INT NULL,
254 blob IMAGE NULL,
74b5397c 255 blob2 IMAGE NULL,
2edf3352 256 clob TEXT NULL,
74b5397c 257 clob2 TEXT NULL,
2edf3352 258 a_memo NTEXT NULL
259)
260],{ RaiseError => 1, PrintError => 1 });
261
262$rs = $schema->resultset('BindType');
263my $id = 0;
264
265foreach my $type (qw( blob clob a_memo )) {
266 foreach my $size (qw( small large )) {
267 $id++;
268
269 lives_ok { $rs->create( { 'id' => $id, $type => $binstr{$size} } ) }
270 "inserted $size $type without dying" or next;
271
272 my $from_db = eval { $rs->find($id)->$type } || '';
273 diag $@ if $@;
274
275 ok($from_db eq $binstr{$size}, "verified inserted $size $type" )
276 or do {
277 my $hexdump = sub {
278 join '', map sprintf('%02X', ord), split //, shift
279 };
280 diag 'Got: ', "\n", substr($hexdump->($from_db),0,255), '...',
281 substr($hexdump->($from_db),-255);
282 diag 'Size: ', length($from_db);
283 diag 'Expected Size: ', length($binstr{$size});
284 diag 'Expected: ', "\n",
285 substr($hexdump->($binstr{$size}), 0, 255),
286 "...", substr($hexdump->($binstr{$size}),-255);
287 };
288 }
289}
290# test IMAGE update
291lives_ok {
292 $rs->search({ id => 0 })->update({ blob => $binstr{small} });
293} 'updated IMAGE to small binstr without dying';
294
295lives_ok {
296 $rs->search({ id => 0 })->update({ blob => $binstr{large} });
297} 'updated IMAGE to large binstr without dying';
298
299# test GUIDs
300lives_ok {
301 $row = $schema->resultset('ArtistGUID')->create({ name => 'mtfnpy' })
302} 'created a row with a GUID';
303
304ok(
305 eval { $row->artistid },
306 'row has GUID PK col populated',
307);
308diag $@ if $@;
309
310my $guid = try { $row->artistid }||'';
311
312ok(($guid !~ /^{.*?}\z/), 'GUID not enclosed in braces')
313 or diag "GUID is: $guid";
314
315ok(
316 eval { $row->a_guid },
317 'row has a GUID col with auto_nextval populated',
318);
319diag $@ if $@;
320
321my $row_from_db = $schema->resultset('ArtistGUID')
322 ->search({ name => 'mtfnpy' })->first;
323
324is try { $row_from_db->artistid }, try { $row->artistid },
325 'PK GUID round trip (via ->search->next)';
326
327is try { $row_from_db->a_guid }, try { $row->a_guid },
328 'NON-PK GUID round trip (via ->search->next)';
329
330$row_from_db = try { $schema->resultset('ArtistGUID')
331 ->find($row->artistid) };
332
333is try { $row_from_db->artistid }, try { $row->artistid },
334 'PK GUID round trip (via ->find)';
335
336is try { $row_from_db->a_guid }, try { $row->a_guid },
337 'NON-PK GUID round trip (via ->find)';
338
339($row_from_db) = $schema->resultset('ArtistGUID')
340 ->search({ name => 'mtfnpy' })->all;
341
342is try { $row_from_db->artistid }, try { $row->artistid },
343 'PK GUID round trip (via ->search->all)';
344
345is try { $row_from_db->a_guid }, try { $row->a_guid },
346 'NON-PK GUID round trip (via ->search->all)';
347
348lives_ok {
349 $row = $schema->resultset('ArtistGUID')->create({
350 artistid => '70171270-4822-4450-81DF-921F99BA3C06',
351 name => 'explicit_guid',
352 });
353} 'created a row with explicit PK GUID';
354
355is try { $row->artistid }, '70171270-4822-4450-81DF-921F99BA3C06',
356 'row has correct PK GUID';
357
358lives_ok {
359 $row->update({ artistid => '70171270-4822-4450-81DF-921F99BA3C07' });
360} "updated row's PK GUID";
361
362is try { $row->artistid }, '70171270-4822-4450-81DF-921F99BA3C07',
363 'row has correct PK GUID';
364
365lives_ok {
366 $row->delete;
367} 'deleted the row';
368
369lives_ok {
370 $schema->resultset('ArtistGUID')->populate([{
371 artistid => '70171270-4822-4450-81DF-921F99BA3C06',
372 name => 'explicit_guid',
373 }]);
374} 'created a row with explicit PK GUID via ->populate in void context';
375
8bcd9ece 376done_testing;
377
4ffa5700 378# clean up our mess
379END {
2edf3352 380 local $SIG{__WARN__} = sub {};
381 if (my $dbh = try { $schema->storage->_dbh }) {
382 (try { $dbh->do("DROP TABLE $_") })
383 for qw/artist artist_guid varying_max_test bindtype_test/;
4ffa5700 384 }
65d35121 385
386 undef $schema;
4ffa5700 387}
388# vim:sw=2 sts=2