File Coverage

Test/DBO.pm
Criterion Covered Total %
statement 396 490 80.8
branch 103 264 39.0
condition 19 58 32.7
subroutine 30 35 85.7
pod 0 15 0.0
total 548 862 63.5


line stmt bran cond sub pod time code
1 10     10   26793 use Test::More;
  10         240552  
  10         109  
2              
3             package # Hide from PAUSE
4             Test::DBO;
5              
6 10     10   3470 use 5.008;
  10         40  
  10         363  
7 10     10   66 use strict;
  10         18  
  10         379  
8 10     10   55 use warnings;
  10         18  
  10         351  
9 10     10   20109 use sigtrap qw(die normal-signals);
  10         15157  
  10         75  
10              
11 10     10   1532 use Scalar::Util qw(blessed reftype);
  10         23  
  10         2129  
12 10     10   58 use Test::More;
  10         19  
  10         70  
13 10     10   10967 use DBIx::DBO;
  10         33  
  10         97  
14             BEGIN {
15 10 50   10   690 require Carp::Heavy if eval "$Carp::VERSION < 1.12";
16              
17             # If we are using a version of Test::More older than 0.82 ...
18 10 50       57 unless (exists $Test::More::{note}) {
19 0         0 eval q#
20             sub Test::More::note {
21             local $Test::Builder::{_print_diag} = $Test::Builder::{_print};
22             Test::More->builder->diag(@_);
23             }
24             *note = \&Test::More::note;
25             no strict 'refs';
26             *{caller(2).'::note'} = \¬e;
27             #;
28 0 0       0 die $@ if $@;
29             }
30              
31             # Set up DebugSQL if requested
32 10 50       48 if ($ENV{DBO_DEBUG_SQL}) {
33 0         0 diag "DBO_DEBUG_SQL=$ENV{DBO_DEBUG_SQL}";
34 0         0 DBIx::DBO->config(DebugSQL => $ENV{DBO_DEBUG_SQL});
35             }
36              
37             # Set up $Carp::Verbose if requested
38 10 50       72 if ($ENV{DBO_CARP_VERBOSE}) {
    50          
39 0         0 diag "DBO_CARP_VERBOSE=$ENV{DBO_CARP_VERBOSE}";
40 0         0 $Carp::Verbose = $ENV{DBO_CARP_VERBOSE};
41             } elsif ($ENV{AUTOMATED_TESTING}) {
42 10         21 $Carp::Verbose = 1;
43             }
44              
45             # Store the last SQL executed, and show debug info
46             DBIx::DBO->config(HookSQL => sub {
47 86         113 my $me = shift;
48 86         18931 my $loc = Carp::short_error_loc();
49 86         4771 my %i = Carp::caller_info($loc);
50 86         3958 $me->config(LastSQL => [$i{'sub'}, @_]);
51 86 50       888 my $dbg = $ENV{DBO_DEBUG_SQL} or return;
52 0         0 my $trace;
53 0 0       0 if ($dbg > 1) {
54 0         0 $trace = "\t$i{sub_name} called at $i{file} line $i{line}\n";
55 0         0 $trace .= "\t$i{sub_name} called at $i{file} line $i{line}\n" while %i = Carp::caller_info(++$loc);
56             } else {
57 0         0 $trace = "\t$i{sub} called at $i{file} line $i{line}\n";
58             }
59 0         0 my $sql = shift;
60 0         0 Test::More::diag "DEBUG_SQL: $sql\nDEBUG_SQL: (".join(', ', map $me->rdbh->quote($_), @_).")\n".$trace;
61 10         83 });
62              
63             {
64 10     10   89 no warnings 'redefine';
  10         18  
  10         1833  
  10         17  
65             # Remove CARP_NOT during tests
66             package # Hide from PAUSE
67             DBIx::DBO;
68             *DBIx::DBO::croak =
69             *DBIx::DBO::Query::croak =
70             *DBIx::DBO::Table::croak =
71             *DBIx::DBO::Row::croak = sub {
72 38 50   38   284 local @DBIx::DBO::DBD::CARP_NOT = () if $Carp::Verbose;
73 38 50       103 local $Carp::CarpLevel = $Carp::CarpLevel + 1 if $Carp::Verbose;
74 38         5147 &Carp::croak;
75 10         74 };
76             # Fix SvREFCNT with Devel::Cover
77             package # Hide from PAUSE
78             DBIx::DBO::Query;
79             *DBIx::DBO::Query::SvREFCNT = sub {
80 48     48   345 return Devel::Peek::SvREFCNT($_[0]) - 1;
81 10 50       15862 } if exists $INC{'Devel/Cover.pm'};
82             }
83             }
84              
85             our $dbd;
86             our $dbd_name;
87             (our $test_db = "DBO_${DBIx::DBO::VERSION}_test_db") =~ s/\W/_/g;
88             (our $test_sch = "DBO_${DBIx::DBO::VERSION}_test_sch") =~ s/\W/_/g;
89             (our $test_tbl = "DBO_${DBIx::DBO::VERSION}_test_tbl") =~ s/\W/_/g;
90             our @_cleanup_sql;
91             our $case_sensitivity_sql = 'SELECT ? LIKE ?';
92             our %can;
93              
94             sub import {
95 11     11   126 my $class = shift;
96 11 50       56 $dbd = shift or return;
97 11         18 $dbd_name = shift;
98 11         49 my %opt = splice @_;
99              
100 11 100       95 grep $_ eq $dbd, DBI->available_drivers
101             or plan skip_all => "No $dbd driver available!";
102              
103             # Catch install_driver errors
104 8         4362 eval { DBI->install_driver($dbd) };
  8         59  
105 8 50       105768 if ($@) {
106 0 0       0 die $@ if $@ !~ /\binstall_driver\b/;
107 0         0 plan skip_all => $@;
108             }
109              
110             # Skip tests with missing module requirements
111 8 100       16 unless (eval { DBIx::DBO::DBD->_require_dbd_class($dbd) }) {
  8         88  
112 1 50       41 if ($@ =~ /^Can't locate ([\w\/]+)\.pm in \@INC /m) {
    50          
    50          
113             # Module is not installed
114 0         0 ($_ = "$1 is required") =~ s'/'::'g;
115             } elsif ($@ =~ /^([\w:]+ version [\d\.]+ required.*?) at /m) {
116             # Module is not correct version
117 0         0 ($_ = $1);
118             } elsif ($@ =~ /^(\Q$dbd_name\E is not yet supported)/m) {
119             # DBM is not yet supported
120 1         3 ($_ = $1);
121             } else {
122 0         0 die $@;
123             }
124 1         11 plan skip_all => "Can't load $dbd driver: $_";
125             }
126              
127             {
128 10     10   84 no strict 'refs';
  10         17  
  10         66606  
  7         12  
129 7         15 *{caller().'::sql_err'} = \&sql_err;
  7         36  
130             }
131              
132 7 100       28 if (exists $opt{tempdir}) {
133 1         1187 require File::Temp;
134 1         31437 my $dir = File::Temp::tempdir('tmp_XXXX', CLEANUP => 1);
135 1 50       541 if (ref $opt{tempdir}) {
136 0         0 ${$opt{tempdir}} = $dir;
  0         0  
137             } else {
138 1 50       15 chdir $dir or die "Can't cd to $dir: $!\n";
139 1     1   64 eval "END { chdir '..' }";
  1         536  
140             }
141             }
142              
143             # Query tests must produce the same result regardless of caching
144 7 50       280 DBIx::DBO->config(CacheQuery => defined $ENV{DBO_CACHE_QUERY} ? $ENV{DBO_CACHE_QUERY} : int rand 2);
145              
146 7 50       32 if (exists $opt{try_connect}) {
147 0         0 try_to_connect($opt{try_connect});
148             }
149              
150 7 100 66     72 note "DBD::$dbd ".${ $::DBD::{$dbd.'::'}{VERSION} } if exists $opt{try_connect} or exists $opt{connect_ok};
  2         23  
151              
152 7 100       709 return unless exists $opt{tests};
153              
154 6 100       63 if (exists $opt{connect_ok}) {
155 2 50       4 my $dbo = connect_ok(@{$opt{connect_ok}}) or plan skip_all => "Can't connect: $DBI::errstr";
  2         11  
156              
157 2         14 plan tests => $opt{tests};
158 2         462 pass "Connect to $dbd_name";
159 2         1027 isa_ok $dbo, 'DBIx::DBO', '$dbo';
160             } else {
161 4         27 plan tests => $opt{tests};
162             }
163             }
164              
165             sub sql_err {
166 0     0 0 0 my $me = shift;
167 0         0 my($cmd, $sql, @bind) = @{$me->config('LastSQL')};
  0         0  
168 0         0 $sql =~ s/^/ /mg;
169 0   0     0 my @err = ($DBI::errstr || $me->rdbh->errstr || '???');
170 0 0       0 unshift @err, 'Bind Values: ('.join(', ', map $me->rdbh->quote($_), @bind).')' if @bind;
171 0         0 unshift @err, "SQL command failed: $cmd", $sql.';';
172 0         0 $err[-1] =~ s/ at line \d+$//;
173 0         0 join "\n", @err;
174             }
175              
176             sub connect_dbo {
177 2     2 0 4 my($dsn, $user, $pass) = @_;
178 2 50       8 defined $dsn or $dsn = '';
179 2         22 DBIx::DBO->connect("DBI:$dbd:$dsn", $user, $pass, {RaiseError => 0});
180             }
181              
182             sub try_to_connect {
183 2     2 0 5 my $dbo_ref = shift;
184 2         19 my @env = map $ENV{"DBO_TEST_\U$dbd\E_$_"}, qw(DSN USER PASS);
185 2 50       14 if (grep defined, @env) {
186 0 0       0 return $$dbo_ref if $$dbo_ref = connect_dbo(@env);
187 0         0 plan skip_all => "Can't connect: $DBI::errstr";
188             }
189 2         20 return undef;
190             }
191              
192             sub connect_ok {
193 2     2 0 7 my $dbo_ref = shift;
194 2   33     10 return try_to_connect($dbo_ref) || ($$dbo_ref = connect_dbo(@_));
195             }
196              
197             sub basic_methods {
198 1     1 0 18 my $dbo = shift;
199              
200 1         13 note 'Testing with: CacheQuery => '.DBIx::DBO->config('CacheQuery');
201              
202             # Create a DBO from DBI handles
203 1         403 isa_ok(DBIx::DBO->new($dbo->{dbh}, $dbo->{rdbh}), 'DBIx::DBO', 'Method DBIx::DBO->new, $dbo');
204              
205 1         589 my $quoted_table = $dbo->{dbd_class}->_qi($dbo, $test_sch, $test_tbl);
206 1         81 my @quoted_cols = map $dbo->{dbd_class}->_qi($dbo, $_), qw(type id name);
207 1         22 my $t;
208 1 50 50     21 my $create_table = "CREATE TABLE $quoted_table ($quoted_cols[1] ".
209             ($can{auto_increment_id} || 'INT NOT NULL').", $quoted_cols[2] VARCHAR(20)".
210             ($can{auto_increment_id} ? '' : ", PRIMARY KEY ($quoted_cols[1])").')';
211              
212             # Create a test table with a multi-column primary key
213 1 50       10 if ($dbo->do("CREATE TABLE $quoted_table ($quoted_cols[2] VARCHAR(20), $quoted_cols[1] INT, $quoted_cols[0] VARCHAR(8), PRIMARY KEY ($quoted_cols[0], $quoted_cols[1]))")) {
214 1         1158 pass 'Create the test table: '.$quoted_table;
215              
216             # Create a table object
217 1         515 $t = $dbo->table([undef, $test_tbl]);
218 1         9 isa_ok $t, 'DBIx::DBO::Table', '$t';
219              
220             # Check the Primary Keys
221 1 50       763 is_deeply $t->{PrimaryKeys}, ['type', 'id'], 'Check PrimaryKeys'
222             or diag Test::DBO::Dump($t);
223              
224             # Recreate our test table
225 1 50 33     649 $dbo->do("DROP TABLE $quoted_table") && $dbo->do($create_table)
      33        
226             or diag sql_err($dbo) or die "Can't recreate the test table!\n";
227              
228             # Remove the created table during cleanup
229 1         400 todo_cleanup("DROP TABLE $quoted_table");
230              
231 1         7 $dbo->{dbd_class}->_get_table_info($dbo, $t->{Schema}, $t->{Name});
232 1         9 $t = $t->new($dbo, [$test_sch, $test_tbl]);
233             }
234             else {
235 0         0 diag sql_err($dbo);
236 0         0 SKIP: {
237 0         0 skip "Can't create a multi-column primary key", 1;
238             }
239              
240             # Create the test table
241 0 0 0     0 ok $dbo->do($create_table), 'Create the test table'
242             or diag sql_err($dbo) or die "Can't create the test table!\n";
243              
244             # Remove the created table during cleanup
245 0         0 todo_cleanup("DROP TABLE $quoted_table");
246              
247             # Create a table object
248 0         0 $t = $dbo->table([$test_sch, $test_tbl]);
249 0         0 isa_ok $t, 'DBIx::DBO::Table', '$t';
250             }
251 1 50       5 die "Couldn't create the DBIx::DBO::Table object!" unless $t;
252              
253 1         6 is $t->dbo, $dbo, 'Method DBIx::DBO::Table->dbo';
254              
255 1         525 pass 'Method DBIx::DBO->do';
256              
257 1         373 ok my $table_info = $dbo->table_info([$test_sch, $test_tbl]), 'Method DBIx::DBO->table_info';
258 1         445 is $table_info, $dbo->table_info($quoted_table), 'Method DBIx::DBO->table_info (quoted)';
259 1 50       443 is $table_info, $dbo->table_info(defined $test_sch ? "$test_sch.$test_tbl" : $test_tbl),
260             'Method DBIx::DBO->table_info (unquoted)';
261              
262             # Insert data
263 1 50       407 $dbo->do("INSERT INTO $quoted_table VALUES (1, 'John Doe')") or diag sql_err($dbo);
264 1 50       211 $dbo->do("INSERT INTO $quoted_table VALUES (?, ?)", undef, 2, 'Jane Smith') or diag sql_err($dbo);
265              
266             # Check the DBO select* methods
267 1         174 my $rv = [];
268 1 50       8 @$rv = $dbo->selectrow_array("SELECT * FROM $quoted_table") or diag sql_err($dbo);
269 1         83 is_deeply $rv, [1,'John Doe'], 'Method DBIx::DBO->selectrow_array';
270              
271 1 50       665 $rv = $dbo->selectrow_arrayref("SELECT * FROM $quoted_table") or diag sql_err($dbo);
272 1         88 is_deeply $rv, [1,'John Doe'], 'Method DBIx::DBO->selectrow_arrayref';
273              
274 1 50       664 $rv = $dbo->selectall_arrayref("SELECT * FROM $quoted_table") or diag sql_err($dbo);
275 1         105 is_deeply $rv, [[1,'John Doe'],[2,'Jane Smith']], 'Method DBIx::DBO->selectall_arrayref';
276              
277             # Insert via table object
278 1 50       1119 $rv = $t->insert(id => 3, name => 'Uncle Arnie') or diag sql_err($t);
279 1         6 ok $rv, 'Method DBIx::DBO::Table->insert';
280              
281 1         452 is_deeply [$t->columns], [qw(id name)], 'Method DBIx::DBO::Table->columns';
282              
283             # Create a column object
284 1         750 my $c = $t->column('id');
285 1         7 isa_ok $c, 'DBIx::DBO::Column', '$c';
286              
287             # Fetch one value from the Table
288 1         556 is $t->fetch_value($t ** 'name', id => 3), 'Uncle Arnie', 'Method DBIx::DBO::Table->fetch_value';
289              
290             # Fetch one value from the Table
291 1         438 is_deeply $t->fetch_hash(id => \3), {id=>3,name=>'Uncle Arnie'}, 'Method DBIx::DBO::Table->fetch_hash';
292              
293             # Fetch one value from the Table
294 1         870 my $r = $t->fetch_row(id => 3, name => \'NOT NULL');
295 1         5 is $r->{name}, 'Uncle Arnie', 'Method DBIx::DBO::Table->fetch_row';
296              
297             # Fetch a column arrayref from the Table
298 1         599 is_deeply $t->fetch_column($t ** 'name', id => 3), ['Uncle Arnie'], 'Method DBIx::DBO::Table->fetch_column';
299              
300             # Advanced insert using a column object
301 1 50       903 $rv = $t->insert($c => {FUNC => '4'}, name => 'NotUsed', name => \"'James Bond'") or diag sql_err($t);
302 1         7 ok $rv, 'Method DBIx::DBO::Table->insert (complex values)';
303 1         459 is $t->fetch_value('name', id => 4), 'James Bond', 'Method DBIx::DBO::Table->insert (remove duplicate cols)';
304              
305             # Delete via table object
306 1 50       432 $rv = $t->delete(id => 3) or diag sql_err($t);
307 1         189 is $rv, 1, 'Method DBIx::DBO::Table->delete';
308              
309 1 50       417 if ($can{auto_increment_id}) {
310 1 50       5 $t->insert(name => 'Vernon Lyon') or diag sql_err($t);
311             } else {
312 0 0       0 $t->insert(id => 5, name => 'Vernon Lyon') or diag sql_err($t);
313             }
314              
315 1 50       7 SKIP: {
316 1         3 skip "No auto-increment $quoted_cols[1] column", 1 unless $can{auto_increment_id};
317 1 50       6 is $t->last_insert_id, 5, 'Method DBIx::DBO::Table->last_insert_id'
318             or $t->delete(name => 'Vernon Lyon'), $t->insert(id => 5, name => 'Vernon Lyon');
319             }
320              
321 1         463 my $bulk_data = $dbo->query($t)->arrayref({ Slice => {} });
322             SKIP: {
323 1 50       304 unless ($can{truncate}) {
  1         6  
324 1 50       7 $t->delete or diag sql_err($t);
325 1         185 skip 'TRUNCATE TABLE is not supported', 1;
326             }
327 0 0       0 $t->truncate or diag sql_err($t);
328 0         0 is $t->fetch_value('id'), undef, 'Method DBIx::DBO::Table->truncate';
329             }
330              
331             # Bulk insert
332 1 50       457 $rv = $t->bulk_insert(rows => [map [@$_{qw(id name)}], @$bulk_data]) or diag sql_err($t);
333 1         45 is $rv, 4, 'Method DBIx::DBO::Table->bulk_insert (ARRAY)';
334 1 50       472 $t->delete or diag sql_err($t);
335              
336 1 50       168 $rv = $t->bulk_insert(rows => \@$bulk_data) or diag sql_err($t);
337 1         12 is $rv, 4, 'Method DBIx::DBO::Table->bulk_insert (HASH)';
338 1 50       2881 $t->delete or diag sql_err($t);
339              
340 1 50       1446 $rv = $t->bulk_insert(columns => [qw(name id)], rows => [map [@$_{qw(name id)}], @$bulk_data]) or diag sql_err($t);
341 1         12 is $rv, 4, 'Method DBIx::DBO::Table->bulk_insert (ARRAY)';
342 1 50       848 $t->delete or diag sql_err($t);
343              
344 1 50       354 $rv = $t->bulk_insert(columns => [qw(name id)], rows => \@$bulk_data) or diag sql_err($t);
345 1         8 is $rv, 4, 'Method DBIx::DBO::Table->bulk_insert (HASH)';
346              
347 1         1024 return $t;
348             }
349              
350             sub advanced_table_methods {
351 1     1 0 9 my $dbo = shift;
352 1         5 my $t = shift;
353              
354 1 50       11 SKIP: {
355 1         4 skip "No test table for advanced table tests", 2 unless $t;
356              
357             # Advanced insert
358 1 50       16 my $rv = $t->insert(id => { FUNC => '? + 3', VAL => 3 }, name => \"'Harry Harrelson'") or diag sql_err($t);
359 1         11 ok $rv, 'Method DBIx::DBO::Table->insert (advanced)';
360              
361 1 50       1107 $t->insert(id => 7, name => 'Amanda Huggenkiss') or diag sql_err($t);
362 1 50       8 $t->insert(id => 8, name => undef) or diag sql_err($t);
363              
364             # Advanced delete
365 1 50       9 $rv = $t->delete(id => \'NOT NULL', name => undef) or diag sql_err($t);
366 1         321 ok $rv, 'Method DBIx::DBO::Table->delete (advanced)';
367             }
368             }
369              
370             sub skip_advanced_table_methods {
371 0     0 0 0 my $dbo = shift;
372 0         0 my $t = shift;
373              
374 0         0 note "No advanced table tests for $dbd_name";
375 0 0       0 $t->insert(id => 6, name => 'Harry Harrelson') or diag sql_err($t);
376 0 0       0 $t->insert(id => 7, name => 'Amanda Huggenkiss') or diag sql_err($t);
377             }
378              
379             sub row_methods {
380 1     1 0 969 my $dbo = shift;
381 1         4 my $t = shift;
382              
383 1         9 my $r = DBIx::DBO::Row->new($dbo, $t->_from);
384 1         9 isa_ok $r, 'DBIx::DBO::Row', '$r (using quoted table name)';
385              
386 1         695 $r = $dbo->row([ @$t{qw(Schema Name)} ]);
387 1         7 isa_ok $r, 'DBIx::DBO::Row', '$r (using table name array)';
388              
389 1         632 $r = $dbo->row($t);
390 1         5 isa_ok $r, 'DBIx::DBO::Row', '$r (using Table object)';
391              
392 1         522 is $r->dbo, $dbo, 'Method DBIx::DBO::Row->dbo';
393              
394 1         593 ok $r->is_empty, 'Method DBIx::DBO::Row->is_empty';
395 1         366 is_deeply [$r->columns], [qw(id name)], 'Method DBIx::DBO::Row->columns';
396              
397 1 50       636 ok $r->load(id => [2, 3], name => 'Jane Smith'), 'Method DBIx::DBO::Row->load' or diag sql_err($r);
398 1         442 is_deeply $$r->{array}, [ 2, 'Jane Smith' ], 'Row loaded correctly';
399              
400             # Access methods
401 1         588 is $r->[1], 'Jane Smith', 'Access row as an arrayref';
402 1         349 is $r->{name}, 'Jane Smith', 'Access row as a hashref';
403 1         354 is $r->value('name'), 'Jane Smith', 'Method DBIx::DBO::Row->value';
404 1         378 is $r->value($t->column('name')), 'Jane Smith', 'Method DBIx::DBO::Row->value (using Table->column)';
405              
406 1 50       397 is $r->update(name => 'Someone Else'), 1, 'Method DBIx::DBO::Row->update' or diag sql_err($r);
407 1 50       558 is_deeply \@$r, [ 2, 'Someone Else' ], 'Row updated correctly (internal)' or diag Test::DBO::Dump($r);
408 1 50       653 $r->load(id => 2) or diag sql_err($r);
409 1 50       8 is_deeply \@$r, [ 2, 'Someone Else' ], 'Row updated correctly (external)' or diag Test::DBO::Dump($r);
410              
411 1 50       954 $r->update(name => 'Nobody', $t ** 'name' => 'Anybody') or diag sql_err($r);
412 1 50       3 is_deeply \@{$r->load(id => 2)}, [ 2, 'Anybody' ], 'Row update removes duplicates' or diag sql_err($r);
  1         5  
413              
414             # UPDATE the primary key and a complex expression, requiring a reload
415 1         864 $r->config(OnRowUpdate => 'reload');
416 1 50       6 $r->update(id => 3, name => \"'Uncle Arnie'") or diag sql_err($r);
417 1 50 33     8 ok !$r->is_empty, 'Row reloaded on update' or $r->load(id => [2, 3]) or diag sql_err($r);
418              
419 1 50       659 ok $r->delete, 'Method DBIx::DBO::Row->delete' or diag sql_err($r);
420 1         659 $t->insert(id => 2, name => 'Jane Smith');
421              
422 1         7 is $r->load(name => 'non-existent'), undef, 'Load non-existent row';
423 1         617 is_deeply $$r->{array}, undef, 'Row is empty again';
424             }
425              
426             sub query_methods {
427 1     1 0 8 my $dbo = shift;
428 1         3 my $t = shift;
429 1         7 my $quoted_table = $t->_from;
430              
431             # Create a query object
432 1         6 my $q = $dbo->query($t);
433 1         7 isa_ok $q, 'DBIx::DBO::Query', '$q';
434              
435 1         534 is $q->dbo, $dbo, 'Method DBIx::DBO::Query->dbo';
436              
437             # Default sql = select everything
438 1         427 is_deeply [$q->columns], [qw(id name)], 'Method DBIx::DBO::Query->columns';
439 1         658 my $sql = $q->sql;
440 1         7 is $sql, "SELECT * FROM $quoted_table", 'Method DBIx::DBO::Query->sql';
441              
442             # Sort the result
443 1         360 $q->order_by('id');
444 1         6 pass 'Method DBIx::DBO::Query->order_by';
445              
446             # Get a valid sth
447 1 50       418 isa_ok $q->_sth, 'DBI::st', '$q->_sth' or diag "SQL command failed: _sth\n $q->{sql}\n".$q->rdbh->errstr;
448              
449             # Get a Row object
450 1         673 my $r = $q->row;
451 1         4 isa_ok $r, 'DBIx::DBO::Row', '$q->row';
452 1         440 my $r_str = "$r";
453              
454 1         6 $q->config(Testing => 123);
455 1         6 is $r->config('Testing'), 123, 'Row gets config from parent Query';
456              
457             # Alter the SQL to ensure the row is detached and rebuilt
458 1         490 $q->order_by('id');
459 1         5 $r = $q->row;
460 1         9 isnt $r_str, "$r", 'Row rebuilds SQL and detaches when a ref still exists';
461 1         1335 $r_str = "$r";
462              
463             # Remove the reference so that the row wont detach
464 1         3 undef $r;
465              
466             # Fetch the first row
467 1         5 $r = $q->fetch;
468 1         75 ok $r->isa('DBIx::DBO::Row'), 'Method DBIx::DBO::Query->fetch';
469 1         528 is $r_str, "$r", 'Re-use the same row object';
470 1         397 is_deeply [$q->columns], [qw(id name)], 'Method DBIx::DBO::Query->columns (after fetch)';
471              
472             # Fetch another row
473 1         783 $r_str = "$r";
474 1         8 $r = $q->fetch;
475 1         6 isnt $r_str, "$r", 'Row detaches during fetch when a ref still exists';
476              
477             # Re-run the query
478 1 50       1183 $q->run or diag sql_err($q);
479 1         6 is $q->fetch->{name}, 'John Doe', 'Method DBIx::DBO::Query->run';
480 1         488 $q->finish;
481 1         5 is $q->fetch->{name}, 'John Doe', 'Method DBIx::DBO::Query->finish';
482              
483             # Count the number of rows
484 1         690 1 while $q->fetch;
485 1         6 is $q->rows, 6, 'Row count is 6';
486              
487             # WHERE clause
488 1 50       490 ok $q->where('name', 'LIKE', \"'%o%'"), 'Method DBIx::DBO::Query->where' or diag sql_err($q);
489              
490             # Parentheses
491 1         430 $q->open_bracket('OR');
492 1         7 $q->where('name', 'LIKE', \"'%a%'");
493 1         4 $q->where('id', '!=', \1);
494 1         4 $q->where('id', '=', undef);
495 1         5 $q->open_bracket('AND');
496 1         6 $q->where('id', '<>', 12345);
497 1         4 $q->where('id', '!=', undef);
498 1         5 $q->where('id', 'NOT IN', [1,22,333]);
499 1         6 $q->where('id', 'NOT BETWEEN', [123,456]);
500 1         8 my $got = $q->col_arrayref({ Columns => [1] });
501 1 50       162 is_deeply $got, [4,5,6], 'Method DBIx::DBO::Query->open_bracket' or diag sql_err($q);
502              
503 1         918 $q->where('id', 'NOT IN', 4444);
504 1         6 ok scalar(() = $q->sql =~ / NOT IN /g) == 1, 'Group multiple IN & NOT IN clauses together';
505              
506 1         708 $q->order_by;
507 1 50       10 is $q->update(id => { FUNC => '? + 10', COL => 'id' }), 3, 'Method DBIx::DBO::Query->update' or diag sql_err($q);
508 1         517 $q->order_by('id');
509              
510 1         5 my $old_sql = $q->sql;
511 1         7 $q->unwhere('name');
512 1         6 is $q->sql, $old_sql, 'Method DBIx::DBO::Query->unwhere (before close_bracket)';
513              
514 1         729 $q->close_bracket;
515 1         6 $q->close_bracket;
516 1         7 $q->unwhere('name');
517 1         8 isnt $q->sql, $old_sql, 'Method DBIx::DBO::Query->close_bracket';
518              
519 1         667 $got = $q->col_arrayref({ Columns => [1] });
520 1         157 is_deeply $got, [2,7,14,15,16], 'Method DBIx::DBO::Query->unwhere';
521              
522             # Reset the Query
523 1         824 $q->reset;
524 1         5 is $q->sql, $dbo->query($t)->sql, 'Method DBIx::DBO::Query->reset';
525              
526             # Group by the first initial
527 1         445 $q->show(\'COUNT(*)');
528 1 50       7 ok(($q->group_by({FUNC => 'SUBSTR(?, 1, 1)', COL => 'name'}), $q->run),
529             'Method DBIx::DBO::Query->group_by') or diag sql_err($q);
530              
531             # Update & Load a Row with aliased columns
532 1         649 $q->show($t, {COL => 'id', AS => 'key'});
533 1         8 $q->group_by;
534 1         6 is_deeply [$q->columns], [qw(id name key)], 'Method DBIx::DBO::Query->columns (with aliases)';
535 1         745 $r = $q->fetch;
536 1         4 is_deeply [$q->columns], [qw(id name key)], 'Method DBIx::DBO::Query->columns (after fetch)';
537 1 50       710 ok $r->update(id => $r->{key}), 'Can update a Row despite using aliases' or diag sql_err($r);
538 1 50       450 ok $r->load(id => 15), 'Can load a Row despite using aliases' or diag sql_err($r);
539              
540 1         431 isa_ok $q ** 'key', 'DBIx::DBO::Column', q{$q ** $alias};
541              
542             # Limit & limit with Offset
543 1         474 $q->show('id');
544 1         5 $q->order_by('id');
545              
546 1         4 $q->limit(3);
547 1         3 $got = [];
548 1         8 for (my $row; $row = $q->fetch; push @$got, $row->[0]) {}
549 1         7 is_deeply $got, [1,2,7], 'Method DBIx::DBO::Query->limit';
550              
551 1         948 $q->limit(3, 2);
552 1         3 $got = [];
553 1         5 for (my $row; $row = $q->fetch; push @$got, $row->[0]) {}
554 1         6 is_deeply $got, [7,14,15], 'Method DBIx::DBO::Query->limit (with offset)';
555              
556 1         871 $q->finish;
557 1         8 return $q;
558             }
559              
560             sub advanced_query_methods {
561 1     1 0 6 my $dbo = shift;
562 1         3 my $t = shift;
563 1         2 my $q = shift;
564 1         8 $q->reset;
565              
566             # Show specific columns only
567 1 50       6 SKIP: {
568 1         2 skip 'COLLATE is not supported', 1 unless $can{collate};
569 1         8 $q->order_by({ COL => 'name', COLLATE => $can{collate} });
570 1 50       6 ok $q->run, 'Method DBIx::DBO::Query->order_by COLLATE' or diag sql_err($q);
571             }
572 1         459 $q->order_by('id');
573 1         7 $q->show({ FUNC => 'UPPER(?)', COL => 'name', AS => 'name' }, 'id', 'name');
574 1 50 33     5 ok $q->run && $q->fetch->{name} eq 'JOHN DOE', 'Method DBIx::DBO::Query->show' or diag sql_err($q);
575              
576 1         516 is $q->row->value($t ** 'name'), 'John Doe', 'Access specific column';
577 1         398 is_deeply [$q->row->columns], [qw(name id name)], 'Method DBIx::DBO::Row->columns (aliased)';
578 1         806 is_deeply [$q->columns], [qw(name id name)], 'Method DBIx::DBO::Query->columns (aliased)';
579              
580             # Show whole tables
581 1         577 $q->show({ FUNC => "'who?'", AS => 'name' }, $t);
582 1         5 is $q->fetch->value($t ** 'name'), 'John Doe', 'Access specific column from a shown table';
583              
584             # Check case sensitivity of LIKE
585 1 50       478 my $case_sensitive = $dbo->selectrow_arrayref($case_sensitivity_sql, undef, 'a', 'A') or diag sql_err($dbo);
586 1         97 $case_sensitive = not $case_sensitive->[0];
587 1 50       12 note "$dbd_name 'LIKE' is".($case_sensitive ? '' : ' NOT').' case sensitive';
588              
589             # WHERE clause
590 1         520 $q->show('id');
591 1         39 ok $q->where('name', 'LIKE', '%a%'), 'Method DBIx::DBO::Query->where LIKE';
592 1 50       682 my $a = $q->col_arrayref or diag sql_err($q);
593 1         124 is_deeply $a, [2,7,14,16], 'Method DBIx::DBO::Query->col_arrayref';
594 1         779 ok $q->where('id', 'BETWEEN', [6, \16]), 'Method DBIx::DBO::Query->where BETWEEN';
595 1 50       445 $a = $q->arrayref or diag sql_err($q);
596 1         227 is_deeply $a, [[7],[14],[16]], 'Method DBIx::DBO::Query->arrayref';
597 1         1072 ok $q->where('name', 'IN', ['Harry Harrelson', 'James Bond']), 'Method DBIx::DBO::Query->where IN';
598 1 50       385 $a = $q->hashref('id') or diag sql_err($q);
599 1         364 is_deeply $a, {14 => {id => 14},16 => {id => 16}}, 'Method DBIx::DBO::Query->hashref';
600              
601             # HAVING clause
602 1 50       1143 my $concat = $dbd eq 'SQLite' ? '? || ?' : 'CONCAT(?, ?)';
603 1         6 my %concat_col = (FUNC => $concat, COL => [qw(id name)]);
604 1 50       7 my $having_col = $dbo->{dbd_class}->_alias_preference($q, 'having') ? 'combo' : \%concat_col;
605 1         10 $q->show('id', 'name', { %concat_col, AS => 'combo'});
606 1         8 $q->group_by('id', 'name');
607 1         5 $q->having($having_col, '=', '14James Bond');
608 1         3 $q->having($having_col, '=', 'ABC-XYZ');
609 1         5 $q->having($having_col, '=', 'XYZ-ABC');
610 1         2 is_deeply [@{$q->fetch}], [14, 'James Bond', '14James Bond'], 'Method DBIx::DBO::Query->having';
  1         6  
611              
612 1         1043 $q->unhaving($having_col, '=', '14James Bond');
613 1         5 is $q->fetch, undef, 'Method DBIx::DBO::Query->unhaving';
614 1         770 $q->unhaving($having_col);
615 1         1 is_deeply [@{$q->fetch}], [14, 'James Bond', '14James Bond'], 'Method DBIx::DBO::Query->unhaving (whole column)';
  1         5  
616              
617 1         988 $q->finish;
618             }
619              
620             sub skip_advanced_query_methods {
621 0     0 0 0 note "No advanced query tests for $dbd_name";
622             }
623              
624             sub join_methods {
625 1     1 0 8 my $dbo = shift;
626 1         3 my $table = shift;
627              
628 1         7 my($q, $t1, $t2) = $dbo->query($table, $table);
629              
630             # DISTINCT clause
631 1         5 $q->order_by('id');
632 1         4 $q->show('id');
633 1         7 $q->distinct(1);
634 1         5 is_deeply $q->arrayref, [[1],[2],[7],[14],[15],[16]], 'Method DBIx::DBO::Query->distinct';
635 1         1739 $q->distinct(0);
636 1         4 $q->show($t1, $t2);
637              
638             # Counting rows
639 1         4 $q->limit(3);
640 1         4 $q->config(CalcFoundRows => 1);
641 1         6 ok $q, 'Comma JOIN';
642 1 50       420 is $q->count_rows, 3, 'Method DBIx::DBO::Query->count_rows' or diag sql_err($q);
643 1 50       541 is $q->found_rows, 36, 'Method DBIx::DBO::Query->found_rows' or diag sql_err($q);
644              
645             # JOIN
646 1         404 $q->join_on($t2, $t1 ** 'id', '=', { FUNC => '?/7.0', VAL => $t2 ** 'id' });
647 1         5 $q->order_by({ COL => $t1 ** 'name', ORDER => 'DESC' });
648 1         4 $q->where($t1 ** 'name', '<', $t2 ** 'name', FORCE => 'OR');
649 1         4 $q->where($t1 ** 'name', '>', $t2 ** 'name', FORCE => 'OR');
650 1         4 $q->where($t1 ** 'name', 'LIKE', '%');
651 1         3 my $r;
652             # Oracle Can't do a SELECT * from a subquery that has "ambiguous" columns (two columns with the same name)
653 1 50       4 $q->show() if $dbd eq 'Oracle';
654 1 0 33     6 SKIP: {
      33        
655 1         3 $q->run or fail 'JOIN ON' or diag sql_err($q) or skip 'No Left Join', 1;
656 1 50 33     8 $r = $q->fetch or fail 'JOIN ON' or skip 'No Left Join', 1;
657              
658 1         6 is_deeply \@$r, [ 1, 'John Doe', 7, 'Amanda Huggenkiss' ], 'JOIN ON';
659 1 50       1067 $r->load($t1 ** id => 2) or diag sql_err($r);
660 1         4 is_deeply \@$r, [ 2, 'Jane Smith', 14, 'James Bond' ], 'Method DBIx::DBO::Row->load';
661             }
662              
663             # LEFT JOIN
664 1         854 ($q, $t1) = $dbo->query($table);
665             # ... "t1" LEFT JOIN ... "t2"
666 1         6 $t2 = $q->join_table($table, 'left');
667             # ... "t1" LEFT JOIN ... "t2" ON "t1"."id" = "t2"."id"/2.0
668 1         5 $q->join_on($t2, $t1 ** 'id', '=', { FUNC => '?/2.0', COL => $t2 ** 'id' });
669 1         8 ok $q->open_join_on_bracket($t2, 'OR'), 'Method DBIx::DBO::Query->open_join_on_bracket';
670             # ... "t1" LEFT JOIN ... "t2" ON "t1"."id" = "t2"."id"/2.0 AND 1 = 2
671 1         619 $q->join_on($t2, \1, '=', \2);
672             # ... "t1" LEFT JOIN ... "t2" ON "t1"."id" = "t2"."id"/2.0 AND (1 = 2 OR 3 = 3)
673 1         6 $q->join_on($t2, \3, '=', \3);
674 1         7 ok $q->close_join_on_bracket($t2), 'Method DBIx::DBO::Query->close_join_on_bracket';
675              
676 1         515 $q->order_by({ COL => $t1 ** 'name', ORDER => 'DESC' });
677 1         6 $q->limit(1, 3);
678              
679 1 0 33     9 SKIP: {
      33        
680 1         1 $q->_sth or diag sql_err($q) or fail 'LEFT JOIN' or skip 'No Left Join', 3;
681 1 50 33     179 $r = $q->fetch or fail 'LEFT JOIN' or skip 'No Left Join', 3;
682              
683 1         5 is_deeply [@$r[0..3]], [14, 'James Bond', undef, undef], 'LEFT JOIN';
684 1         904 is $r->_column_idx($t2 ** 'id'), 2, 'Method DBIx::DBO::Row->_column_idx';
685 1         380 is $r->value($t2 ** 'id'), undef, 'Method DBIx::DBO::Row->value';
686              
687             # Update the LEFT JOINed row
688 1 50       11 SKIP: {
689 1         383 skip "Multi-table UPDATE is not supported by $dbd_name", 1 unless $can{multi_table_update};
690 0 0       0 ok $r->update($t1 ** 'name' => 'Vernon Wayne Lyon'), 'Method DBIx::DBO::Row->update' or diag sql_err($r);
691             }
692             }
693              
694 1         306 $q->finish;
695             }
696              
697             sub todo_cleanup {
698 1     1 0 3 my $sql = shift;
699 1         5 unshift @_cleanup_sql, $sql;
700             }
701              
702             sub cleanup {
703 1     1 0 7 my $dbo = shift;
704              
705 1         5 note 'Doing cleanup';
706 1         197 for my $sql (@_cleanup_sql) {
707 1 50       6 $dbo->do($sql) or diag sql_err($dbo);
708             }
709              
710 1         453 $dbo->disconnect;
711 1   33     11 ok !defined $dbo->{dbh} && !defined $dbo->{rdbh}, 'Method DBIx::DBO->disconnect';
712             }
713              
714             sub Dump {
715 0     0 0 0 my($val, $var) = @_;
716 0 0 0     0 if (blessed $val and !defined $var) {
717 0 0       0 if ($val->isa('DBIx::DBO')) {
    0          
    0          
    0          
718 0         0 $var = 'dbo';
719             } elsif ($val->isa('DBIx::DBO::Table')) {
720 0         0 $var = 't';
721             } elsif ($val->isa('DBIx::DBO::Query')) {
722 0         0 $var = 'q';
723             } elsif ($val->isa('DBIx::DBO::Row')) {
724 0         0 $var = 'r';
725             }
726             }
727 0 0       0 $var = 'dump' unless defined $var;
728 0         0 require Data::Dumper;
729 0         0 local $Data::Dumper::Sortkeys = 1;
730 0         0 local $Data::Dumper::Quotekeys = 0;
731 0         0 my $d = Data::Dumper->new([$val], [$var]);
732 0 0       0 if (ref $val) {
733 0         0 my %seen;
734 0         0 my @_no_recursion = ($val);
735 0 0       0 if (reftype $val eq 'ARRAY') { _Find_Seen(\%seen, \@_no_recursion, $_) for @$val }
  0 0       0  
    0          
736 0         0 elsif (reftype $val eq 'HASH') { _Find_Seen(\%seen, \@_no_recursion, $_) for values %$val }
737 0         0 elsif (reftype $val eq 'REF') { _Find_Seen(\%seen, \@_no_recursion, $$val) }
738 0         0 $d->Seen(\%seen);
739             }
740 0 0       0 defined wantarray ? return $d->Dump : print $d->Dump;
741             }
742              
743             sub _Find_Seen {
744 0     0   0 my($seen, $_no_recursion, $val) = @_;
745 0 0       0 return unless ref $val;
746 0         0 for (@$_no_recursion) {
747 0 0       0 return if $val == $_;
748             }
749 0         0 push @$_no_recursion, $val;
750              
751 0 0       0 if (blessed $val) {
752 0 0       0 if ($val->isa('DBIx::DBO')) {
    0          
    0          
    0          
753 0         0 $seen->{dbo} = $val;
754 0         0 return;
755             } elsif ($val->isa('DBIx::DBO::Table')) {
756 0         0 my $t = 1;
757 0         0 while (my($k, $v) = each %$seen) {
758 0 0       0 next if $k !~ /^t\d+$/;
759 0 0       0 return if $val == $v;
760 0         0 $t++;
761             }
762 0         0 $seen->{"t$t"} = $val;
763 0         0 return;
764             } elsif ($val->isa('DBIx::DBO::Query')) {
765 0         0 $seen->{q} = $val;
766 0         0 return;
767             } elsif ($val->isa('DBIx::DBO::Row')) {
768 0         0 $seen->{r} = $val;
769 0         0 return;
770             }
771             }
772 0 0       0 if (reftype $val eq 'ARRAY') { _Find_Seen($seen, $_no_recursion, $_) for @$val }
  0 0       0  
    0          
773 0         0 elsif (reftype $val eq 'HASH') { _Find_Seen($seen, $_no_recursion, $_) for values %$val }
774 0         0 elsif (reftype $val eq 'REF') { _Find_Seen($seen, $_no_recursion, $$val) }
775             }
776              
777             # When testing via Sponge, use fake tables
778             package # Hide from PAUSE
779             DBIx::DBO::DBD::Sponge;
780             sub _get_table_schema {
781 11     11   26 return;
782             }
783             my $fake_table_info = {
784             PrimaryKeys => [],
785             Columns => [ 'id', 'name', 'age' ],
786             Column_Idx => { id => 1, name => 2, age => 3 },
787             };
788             sub _get_table_info {
789 5     5   13 my($class, $me, $schema, $table) = @_;
790 5 100       38 return $class->SUPER::_get_table_info($me, $schema, $table) if $table ne $Test::DBO::test_tbl;
791             # Fake table info
792 4   33     39 return $me->{TableInfo}{''}{$table} ||= $fake_table_info;
793             }
794              
795             # When testing via MySponge, fake table contents
796             package # Hide from PAUSE
797             MySponge::db;
798             @MySponge::ISA = ('DBI');
799             @MySponge::db::ISA = ('DBI::db');
800             @MySponge::st::ISA = ('DBI::st');
801             my @cols;
802             my @rows;
803             sub setup {
804 3     3   23 @cols = @{shift()};
  3         13  
805 3         11 @rows = @_;
806             }
807             sub prepare {
808 6     6   39 my($dbh, $sql, $attr) = @_;
809 6   50     29 $attr ||= {};
810 6   50     35 $attr->{NAME} ||= \@cols;
811 6   50     29 $attr->{rows} ||= \@rows;
812 6         80 $dbh->SUPER::prepare($sql, $attr);
813             }
814              
815             1;