File Coverage

blib/lib/DBIx/Abstract.pm
Criterion Covered Total %
statement 333 603 55.2
branch 128 310 41.2
condition 21 72 29.1
subroutine 26 45 57.7
pod 24 31 77.4
total 532 1061 50.1


line stmt bran cond sub pod time code
1             # ABSTRACT: DBI SQL abstraction
2             package DBIx::Abstract;
3             $DBIx::Abstract::VERSION = '1.04';
4 1     1   1208239 use DBI;
  1         20772  
  1         74  
5 1     1   13 use Scalar::Util 'weaken';
  1         2  
  1         124  
6 1     1   1003 use Check::ISA qw( obj_does );
  1         26996  
  1         6  
7 1     1   225 use strict;
  1         2  
  1         28  
8 1     1   5 use warnings;
  1         2  
  1         37092  
9              
10             our $AUTOLOAD;
11              
12             sub ___drivers {
13 0     0   0 my ( $driver, $config ) = @_;
14 0         0 my %drivers = (
15              
16             # Feel free to add new drivers... note that some DBD data_sources
17             # do not translate well (eg Oracle).
18             mysql => "dbi:mysql:$config->{dbname}:$config->{host}:$config->{port}",
19             msql => "dbi:msql:$config->{dbname}:$config->{host}:$config->{port}",
20              
21             # According to DBI, drivers should use the below if they have no
22             # other preference. It is ODBC style.
23             DEFAULT => "dbi:$driver:"
24             );
25              
26             # Make Oracle look a little bit like other DBs.
27             # Right now we only have one hack, but I can imagine there being
28             # more...
29 0 0       0 if ( $driver eq 'Oracle' ) {
30 0   0     0 $config->{'sid'} ||= delete( $config->{'dbname'} );
31             }
32              
33 0         0 my @keys;
34 0         0 foreach ( sort keys %$config ) {
35 0 0       0 next if /^user$/;
36 0 0       0 next if /^password$/;
37 0 0       0 next if /^driver$/;
38 0         0 push( @keys, "$_=$config->{$_}" );
39             }
40 0         0 $drivers{'DEFAULT'} .= join( ';', @keys );
41 0 0       0 if ( $drivers{$driver} ) {
42 0         0 return $drivers{$driver};
43             }
44             else {
45 0         0 return $drivers{'DEFAULT'};
46             }
47             }
48              
49             sub connect {
50 4     4 1 431 my $class = shift;
51 4         9 my ( $config, $options ) = @_;
52 4         6 my ( $dbh, $data_source, $user, $pass, $driver, $dbname, $host, $port );
53 4         10 my $self = {};
54              
55 4 50       32 if ( !defined($config) ) {
    100          
    50          
    0          
56 0         0 require Carp;
57 0         0 Carp::croak( "DBIx::Abstract->connect A connection configuration must be provided." );
58             }
59             elsif ( ref($config) eq 'HASH' ) {
60 2 50       10 if ( $config->{'dbh'} ) {
61 0         0 $dbh = $config->{'dbh'};
62             }
63             else {
64 2   33     14 $user = $config->{'user'} || $config->{'username'};
65 2   33     13 $pass = $config->{'password'} || $config->{'pass'};
66 2 50 33     14 if ( !defined( $config->{'user'} ) && $config->{'password'} ) {
67 0         0 $pass = undef;
68             }
69 2 50       7 if ( exists( $config->{'dsn'} ) ) {
70 2         4 $data_source = $config->{'dsn'};
71             }
72             else {
73 0   0     0 $driver = $config->{'driver'} || 'mysql';
74              
75             # Forcing these to be passed, one way or another, seems odd
76             # to me. To me it seems like it would be better to not pass
77             # them at all, if they weren't passed to us. However I
78             # suspect that this is here to fix some obscure bug that I
79             # can no longer remember.
80 0   0     0 $dbname = $config->{'dbname'} || $config->{'db'} || '';
81 0   0     0 $host = $config->{'host'} || '';
82 0   0     0 $port = $config->{'port'} || '';
83              
84 0         0 $data_source = ___drivers(
85             $config->{'driver'}, {
86             %$config,
87             driver => $driver,
88             dbname => $dbname,
89             host => $host,
90             port => $port,
91             }
92             );
93             }
94             }
95             }
96             elsif ( obj_does( $config, 'DBI::db' ) ) {
97 2         82 $dbh = $config;
98             }
99             elsif ( ref($config) ) {
100 0         0 die "DBIx::Abstract->connect Config must be a hashref or a DBI object, not a "
101             . ref($config) . "ref\n";
102             }
103             else {
104 0         0 warn "DBIx::Abstract->connect Config should be hashref or a DBI object. Using scalar is deprecated.\n";
105 0         0 $data_source = $config;
106 0         0 $config = {};
107             }
108              
109 4 100       17 if ($data_source) {
    50          
110 2         15 $dbh = DBI->connect( $data_source, $user, $pass );
111             }
112             elsif ( !$dbh ) {
113 0         0 die "Could not understand data source.\n";
114             }
115              
116 4 50       15370 if ( !$dbh ) { return 0 }
  0         0  
117 4         14 bless( $self, $class );
118 4 100 66     28 if ( ref($config) eq 'HASH' and !$config->{'dbh'} ) {
119 2         23 $self->{'connect'} = {
120             driver => $config->{'driver'},
121             dbname => $config->{'dbname'},
122             host => $config->{'host'},
123             port => $config->{'port'},
124             user => $user,
125             password => $pass,
126             data_source => $data_source,
127             };
128             }
129             else {
130 2         9 $self->{'connect'} = { dbh => 1 };
131             }
132 4         11 $self->{'dbh'} = $dbh;
133 4         16 $self->opt( loglevel => 0 );
134 4         23 foreach ( sort keys %$options ) {
135 2         7 $self->opt( $_, $options->{$_} );
136             }
137 4         9 my @log;
138 4 100       35 if ( exists( $config->{'dsn'} ) ) {
    50          
139 2 50       11 push( @log, 'dsn=>' . $data_source ) if defined($data_source);
140             }
141             elsif ( ref($config) eq 'HASH' ) {
142 0         0 foreach (qw( driver host port db )) {
143 0 0       0 push( @log, $_ . '=>' . $config->{$_} ) if defined( $config->{$_} );
144             }
145             }
146 4 50       46 push( @log, 'user=>', $user ) if defined($user);
147 4 50       9 push( @log, 'password=>', $pass ) if defined($pass);
148 4         12 $self->__logwrite( 5, 'Connect', @log );
149 4         10 $self->{'Active'} = 1;
150 4         17 return $self;
151             }
152              
153             sub ensure_connection {
154 0     0 1 0 my $self = shift;
155 0         0 my $result = 0;
156 0         0 my $connected = $self->connected;
157 0 0       0 if ( $self->connected ) {
158 0         0 eval { ($result) = $self->select('1')->fetchrow_array };
  0         0  
159 0 0       0 eval { $self->disconnect unless $result };
  0         0  
160             }
161 0 0       0 unless ($result) {
162 0         0 $result = $self->reconnect;
163             }
164 0 0       0 if ($result) {
165 0 0       0 if ( $result == 1 ) {
    0          
166 0         0 $self->__logwrite( 5, 'ensure_connection', 'functioning' );
167             }
168             elsif ($connected) {
169 0         0 $self->__logwrite( 5, 'ensure_connection',
170             'failed; reestablished' );
171             }
172             else {
173 0         0 $self->__logwrite( 5, 'ensure_connection', 'reestablished' );
174             }
175             }
176             else {
177 0 0       0 if ($connected) {
178 0         0 $self->__logwrite( 0, 'ensure_connection',
179             'failed; could not reestablish' );
180             }
181             else {
182 0         0 $self->__logwrite( 0, 'ensure_connection',
183             'could not reestablish' );
184             }
185 0         0 die "Could not ensure connection.\n";
186             }
187 0         0 return $self;
188             }
189              
190             sub connected {
191 5     5 1 3222 my $self = shift;
192 5         9 my $connected;
193              
194             # Some drivers (mysqlPP) don't properly record their Active status.
195 5 50       107 if ( $self->{'dbh'}->{'Driver'}->{'Name'} eq 'mysqlPP' ) {
196 0 0       0 $connected = eval { ( $self->{'dbh'} and $self->{'Active'} ) };
  0         0  
197             }
198             else {
199 5 50       11 $connected = eval { ( $self->{'dbh'} and $self->{'dbh'}->{'Active'} ) };
  5         69  
200             }
201 5 50       23 $connected = 0 if $@;
202 5         23 $self->__logwrite( 5, 'connected', $connected );
203 5         33 return $connected;
204             }
205              
206             sub reconnect {
207 1     1 1 8 my $self = shift;
208 1         3 my $dbh;
209 1 50 33     6 if ( !$self->connected and $self->{'connect'}{'data_source'} ) {
210 1         10 $dbh = DBI->connect(
211             $self->{'connect'}{'data_source'},
212             $self->{'connect'}{'user'},
213             $self->{'connect'}{'password'}
214             );
215             }
216 1 50       374 if ( !$dbh ) {
217 0         0 $self->__logwrite( 5, 'reconnect', 'fail' );
218 0         0 return 0;
219             }
220 1         5 $self->__logwrite( 5, 'reconnect', 'success' );
221 1         3 $self->{'dbh'} = $dbh;
222              
223 1         27 my @tolog;
224 1         4 foreach (qw( host port dbname user password )) {
225 5 50       18 push( @tolog, $self->{'connect'}{$_} ) if $self->{'connect'}{$_};
226             }
227 1         4 $self->__logwrite( 5, 'Reconnect', @tolog );
228 1         8 $self->{'Active'} = 1;
229 1         12 return $self;
230             }
231              
232             sub DESTROY {
233 6     6   736 my $self = shift;
234 6 100 66     31 if ( exists( $self->{'DESTRUCTION'} ) and $self->{'DESTRUCTION'} ) {
235 1         6 return -1;
236             }
237 5         11 $self->{'DESTRUCTION'} = 1;
238 5 100       17 if ( !$self->{'ORIG'} ) {
239 4 100       15 if ( $self->{'CLONES'} ) {
240 1         3 foreach ( @{ $self->{'CLONES'} } ) {
  1         5  
241 0 0       0 if ( ref($_) ) {
242 0 0       0 if ( $_->DESTROY == -1 ) {
243 0         0 warn
244             "Error: DBIx::Abstract tried to recurse into $_ from $self during DESTROY \n";
245             }
246             }
247             else {
248              
249             # Shouldn't be possible to get here... but Perl's destruction is
250             # a bit weird. I guess I wouldn't expect less from the
251             # apocalypse.
252             # warn "Error: DBIx::Abstract clone not object\n";
253             }
254 0         0 $_ = undef;
255             }
256             }
257 4 100       28 $self->{'sth'}->finish if ref( $self->{'sth'} );
258              
259             # Close our handle if we opened it and its still around
260 4 100 66     26 if ( !$self->{'connect'}{'dbh'} and defined( $self->{'dbh'} ) ) {
261 2         11 $self->{'dbh'}->disconnect;
262             }
263             }
264             else {
265 1         4 my $new = [];
266 1         2 foreach ( @{ $self->{'ORIG'}->{'CLONES'} } ) {
  1         5  
267 1 50 33     16 if ( defined($_) and ref($_) and $self ne $_ ) {
      33        
268 0         0 push( @$new, $_ );
269             }
270             }
271 1         4 $self->{'ORIG'}->{'CLONES'} = $new;
272             }
273 5 100       28 $self->{'sth'}->finish if ref( $self->{'sth'} );
274             ## Apparently this can cause $self->{'dbh'} to be deleted prior to
275             ## disconnect being called. Bleah.
276             # delete($self->{'dbh'});
277 5         35 delete( $self->{'sth'} );
278              
279             # delete($self->{'connect'});
280 5         15 delete( $self->{'options'} );
281 5         9 delete( $self->{'MODQUERY'} );
282 5         11 delete( $self->{'ORIG'} );
283 5         7 delete( $self->{'CLONES'} );
284 5         268 return 0;
285             }
286              
287             sub clone {
288 1     1 1 3 my $self = shift;
289 1         2 my $class = ref($self);
290 1         6 my $newself = {%$self};
291 1         4 delete( $newself->{'CLONES'} );
292 1         2 delete( $newself->{'ORIG'} );
293 1         4 bless $newself, $class;
294 1 50       6 if ( !$self->{'ORIG'} ) {
295 1         3 $newself->{'ORIG'} = $self;
296             }
297             else {
298 0         0 $newself->{'ORIG'} = $self->{'ORIG'};
299             }
300 1         7 weaken( $newself->{'ORIG'} );
301              
302 1         1 push( @{ $newself->{'ORIG'}->{'CLONES'} }, $newself );
  1         4  
303 1         5 weaken(
304 1         3 $newself->{'ORIG'}->{'CLONES'}[ $#{ $newself->{'ORIG'}->{'CLONES'} } ]
305             );
306              
307 1         5 $self->__logwrite( 5, 'Cloned' );
308 1         4 return $newself;
309             }
310              
311             my %valid_opts = map( { $_ => 1 } qw(
312             loglevel logfile saveSQL useCached delaymods
313             ) );
314              
315             sub opt {
316 666     666 1 792 my $self = shift;
317 666         823 my ( $key, $value ) = @_;
318 666 50       1279 if ( ref($key) ) {
319 0         0 $value = $key->{'value'};
320 0         0 $key = $key->{'key'};
321             }
322 666         874 my $ret;
323 666 50       10142 if ( $valid_opts{$key} ) {
    0          
324 666         1189 $ret = $self->{'options'}{$key};
325             }
326             elsif ( exists( $self->{'dbh'}{$key} ) ) {
327 0         0 $ret = $self->{'dbh'}{$key};
328             }
329             else {
330 0         0 die "DBIx::Abstract->opt Unknown option $key\n";
331             }
332 666 100       1394 if ( defined($value) ) {
333 6 50       16 if ( $valid_opts{$key} ) {
334 6         17 $self->{'options'}{$key} = $value;
335             }
336             else {
337 0         0 eval { $self->{'dbh'}->{$key} = $value };
  0         0  
338 0 0       0 if ($@) {
339 0         0 warn $@;
340 0         0 return $ret;
341             }
342             }
343 6 50       31 $self->__logwrite(
    50          
    100          
344             5,
345             'Option change',
346             $key ? $key : '',
347             $ret ? $ret : '',
348             $value ? $value : ''
349             );
350             }
351 666         37916 return $ret;
352             }
353              
354             sub __literal_query {
355 17     17   37 my $self = shift;
356             # This actually makes a query
357             # All of the other related query functions (eventually) call this
358 17         49 my ( $sql, @bind_values ) = @_;
359 17         34 my $sth;
360 17 50       45 if ( $self->opt('saveSQL') ) {
361 0         0 my @bind_copy = @bind_values;
362 0         0 $self->{'lastsql'} = $sql;
363 0         0 $self->{'lastsql'} =~ s/\?/$self->quote(shift(@bind_copy))/eg;
  0         0  
364             }
365 17 50       49 if ( $self->opt('useCached') ) {
366 0         0 $sth = $self->{'dbh'}->prepare_cached($sql);
367             }
368             else {
369 17         193 $sth = $self->{'dbh'}->prepare($sql);
370             }
371 17 50       3074 if ( !$sth ) {
372 0         0 eval('use Carp;');
373 0         0 die 'DBIx::Abstract (prepare): '
374             . $self->{'dbh'}->errstr . "\n"
375             . " SQL: $sql\n"
376             . "STACK TRACE\n"
377             . Carp::longmess() . "\n";
378             }
379 17 50       1898167 if ( !$sth->execute(@bind_values) ) {
380 0         0 eval('use Carp;');
381 0         0 die 'DBIx::Abstract (execute): '
382             . $sth->errstr . "\n"
383             . " SQL: $sql\n"
384             . "STACK TRACE\n"
385             . Carp::longmess() . "\n";
386             }
387 17         144 $self->{'sth'} = $sth;
388 17         3421 return $self;
389             }
390              
391             sub __mod_query {
392 10     10   25 my $self = shift;
393             # This is used by queries that make changes.
394             # This way we can process these tasks later if we want to.
395 10         112 my ( $sql, @bind_params ) = @_;
396 10 50       31 if ( $self->opt('delaymods') ) {
397 0 0       0 if ( $self->{'ORIG'} ) { $self = $self->{'ORIG'} }
  0         0  
398 0         0 push( @{ $self->{'MODQUERY'} }, [ $sql, @bind_params ] );
  0         0  
399             }
400             else {
401 10         36 $self->__literal_query( $sql, @bind_params );
402             }
403 10         50 return $self;
404             }
405              
406             sub query {
407 4     4 1 1632 my $self = shift;
408 4         12 my ( $sql, @bind_params ) = @_;
409 4 50       16 if ( ref($sql) eq 'HASH' ) {
410 0         0 @bind_params = @{ $sql->{'bind_params'} };
  0         0  
411 0         0 $sql = $sql->{'sql'};
412             }
413 4         23 $self->__logwrite_sql( 3, $sql, @bind_params );
414 4         21 return $self->__literal_query( $sql, @bind_params );
415             }
416              
417             sub __logwrite {
418 275     275   475 my $self = shift;
419             # This writes to the log file if the loglevel is greater then 0
420             # and the logfile has been set.
421             # LOGLEVEL: 0 -- Fatal errors only
422             # LOGLEVEL: 1 -- Modifications
423             # LOGLEVEL: 2 -- And selects
424             # LOGLEVEL: 3 -- And user created queries
425             # LOGLEVEL: 4 -- And results of queries
426             # LOGLEVEL: 5 -- And other misc commands
427             # LOGLEVEL: 6 -- Internals of commands
428 275         482 my ( $level, @log ) = @_;
429 275 50       633 $level = 5 if $level + 0 ne $level;
430 275 50       520 if ( $#log == -1 ) { @log = ('Something happened') }
  0         0  
431              
432             # Write a line to the log file
433 275 100 100     1625 if ( $self->opt('logfile') && $self->opt('loglevel') >= $level ) {
434 22         62 local *LOG;
435 22 50       54 if ( open( LOG, '>>' . $self->opt('logfile') ) ) {
436 22         976 print LOG join( chr(9), scalar( localtime() ), $level, @log ), "\n";
437 22         1607 close(LOG);
438             }
439             }
440 275         785 return $self;
441             }
442              
443             sub __logwrite_sql {
444 17     17   43 my $self = shift;
445 17         56 my ($level, $sql, @bind ) = @_;
446 17   50     55 $level ||= 5;
447 17 50       51 if ( !defined($sql) ) {
448 0         0 $sql = 'Something happened, and I thought it was SQL';
449             }
450              
451             # Write a line to the log file
452 17 50 33     71 if ( $self->opt('logfile') && $self->opt('loglevel') >= $level ) {
453 17         59 local *LOG;
454 17 50       51 if ( open( LOG, '>>' . $self->opt('logfile') ) ) {
455 17         37 my $logsql = $sql;
456 17         45 my @bind_copy = @bind;
457 17         130 $logsql =~ s/\?/$self->quote(shift(@bind_copy))/eg;
  33         314  
458 17 50       181 unshift( @bind_copy, 'EXTRA BOUND PARAMS: ' ) if @bind_copy;
459 17         992 print LOG
460             join( chr(9), scalar( localtime() ), $level, $logsql,
461             @bind_copy ), "\n";
462 17         682 close(LOG);
463             }
464             }
465 17         53 return $self;
466             }
467              
468             sub run_delayed {
469 0     0 1 0 my $self = shift;
470 0 0       0 if ( $self->{'ORIG'} ) { $self = $self->{'ORIG'} }
  0         0  
471 0         0 $self->__logwrite( 5, 'Run delayed' );
472 0         0 foreach ( @{ $self->{'MODQUERY'} } ) {
  0         0  
473 0         0 $self->__literal_query(@$_);
474             }
475 0         0 return $self;
476             }
477              
478             sub __where {
479 32     32   52 my $self = shift;
480 32         45 my ($where, $int ) = @_;
481              
482             # $where == This is either a scalar, hash-ref or array-ref
483             # If it is a scalar, then it is used as the literal where.
484             # If it is a hash-ref then the key is the field to check,
485             # the value is either a literal value to compare equality to,
486             # or an array-ref to an array of operator and value.
487             # {first=>'joe',age=>['>',26],last=>['like',q|b'%|]}
488             # Would produce:
489             # WHERE first=? AND age > ? AND last is like ?
490             # and add joe, 26 and b'% to the bind_params list
491             # If it is an array-ref then it is an array of hash-refs and
492             # connectors:
493             # [{first=>'joe',age=>['>',26]},'OR',{last=>['like',q|b'%|]}]
494             # Would produce:
495             # WHERE (first=? AND age > ?) OR (last like ?)
496             # and add joe, 26 and b'% to the bind_params list
497 32         46 my $result = '';
498 32         38 my @bind_params;
499 32   100     81 $int ||= 0;
500              
501 32 100       60 if ( $int > 20 ) {
502 1         6 $self->__logwrite( 0, 'Where parser iterated too deep (limit of 20)' );
503 1         41 die
504             "DBIx::Abstract Where parser iterated too deep, circular reference in where clause?\n";
505             }
506              
507 31         75 $self->__logwrite( 6, 'Where called with: ', $where );
508              
509 31 100       90 if ( ref($where) eq 'ARRAY' ) {
    50          
510 26         58 $self->__logwrite( 7, 'Where is array...' );
511 26         49 foreach (@$where) {
512 76 100       198 if ( ref($_) eq 'HASH' ) {
    100          
513 27         50 $self->__logwrite( 7, 'Found where component of type hash' );
514 27         64 my ( $moreres, @morebind ) = $self->__where_hash($_);
515 27 50       83 $result .= "($moreres)" if $moreres;
516 27         60 push( @bind_params, @morebind );
517             }
518             elsif ( ref($_) eq 'ARRAY' ) {
519 23         58 $self->__logwrite( 7, 'Found where component of type array' );
520 23         221 my ( $moreres, @morebind ) = $self->__where( $_, $int + 1 );
521 3 50       12 $result .= "($moreres)" if $moreres;
522 3         9 push( @bind_params, @morebind );
523             }
524             else {
525 26         108 $self->__logwrite( 7,
526             'Found where component of type literal: ' . $_ );
527 26         66 $result .= " $_ ";
528             }
529             }
530             }
531             elsif ( ref($where) eq 'HASH' ) {
532 5         14 $self->__logwrite( 7, 'Where is hash...' );
533 5         19 my ( $moreres, @morebind ) = $self->__where_hash($where);
534 5         9 $result = $moreres;
535 5         13 @bind_params = @morebind;
536             }
537             else {
538 0         0 $self->__logwrite( 7, 'Where is literal...' );
539 0         0 $result = $where;
540             }
541 11 100       34 $self->__logwrite( 6, $int ? 0 : 1, 'Where returning with: ', $result );
542 11 50       22 if ($result) {
543 11 100       49 return ( $int ? '' : ' WHERE ' ) . $result, @bind_params;
544             }
545             else {
546 0         0 return '';
547             }
548             }
549              
550             sub __where_hash {
551 32     32   36 my $self = shift;
552 32         37 my ($where ) = @_;
553 32         35 my $ret;
554             my @bind_params;
555 32         65 $self->__logwrite( 7, 'Processing hash' );
556 32         99 foreach ( sort keys %$where ) {
557 32         83 $self->__logwrite( 7, 'key', $_, 'value', $where->{$_} );
558 32 50       69 if ($ret) { $ret .= ' AND ' }
  0         0  
559 32         56 $ret .= "$_ ";
560 32 100       80 if ( ref( $where->{$_} ) eq 'ARRAY' ) {
561 27         37 $self->__logwrite( 7, 'Value is array', @{ $where->{$_} } );
  27         76  
562 27         62 $ret .= $where->{$_}[0] . ' ';
563 27 50       66 if ( ref( $where->{$_}[1] ) eq 'SCALAR' ) {
564 0         0 $ret .= ${ $where->{$_}[1] };
  0         0  
565             }
566             else {
567 27         30 $ret .= '?';
568 27         90 push( @bind_params, $where->{$_}[1] );
569             }
570             }
571             else {
572 5         13 $self->__logwrite( 7, 'Value is literal', $where->{$_} );
573 5 50       16 if ( defined( $where->{$_} ) ) {
574 5         5 $ret .= '= ';
575 5 50       12 if ( ref( $where->{$_} ) eq 'SCALAR' ) {
576 0         0 $ret .= ${ $where->{$_} };
  0         0  
577             }
578             else {
579 5         13 $ret .= '?';
580 5         17 push( @bind_params, $where->{$_} );
581             }
582             }
583             else {
584 0         0 $ret .= 'IS NULL';
585             }
586             }
587             }
588 32 50       82 if ( $ret ne '()' ) {
589 32         111 return $ret, @bind_params;
590             }
591             else {
592 0         0 return '';
593             }
594             }
595              
596             sub delete {
597 1     1 1 575 my $self = shift;
598 1         3 my ($table, $where ) = @_;
599              
600             # $table == Name of table to update
601             # $where == One of my handy-dandy standard where's. See __where.
602 1 50       4 if ( ref($table) ) {
603 0         0 $where = $table->{'where'};
604 0         0 $table = $table->{'table'};
605             }
606              
607 1 50       4 $table or die 'DBIx::Abstract: delete must have table';
608              
609 1         6 my ( $res, @bind_params ) = $self->__where($where);
610 1         4 my $sql = "DELETE FROM $table" . $res;
611 1         4 $self->__logwrite_sql( 1, $sql, @bind_params );
612 1         5 $self->__mod_query( $sql, @bind_params );
613 1         9 return $self;
614             }
615              
616             sub insert {
617 8     8 1 1584 my $self = shift;
618 8         26 my ($table, $fields ) = @_;
619              
620             # $table == Name of table to update
621             # $fields == A reference to a hash of field/value pairs containing the
622             # new values for those fields.
623 8         19 my (@bind_params);
624 8 50       34 if ( ref($table) ) {
625 0         0 $fields = $table->{'fields'};
626 0         0 $table = $table->{'table'};
627             }
628              
629 8 50       25 $table or die 'DBIx::Abstract: insert must have table';
630              
631 8         30 my $sql = "INSERT INTO $table ";
632 8 50 0     39 if ( ref($fields) eq 'HASH' ) {
    0          
633 8         87 my @keys = sort keys %$fields;
634 8         31 my @values = map {$fields->{$_}} @keys;
  24         79  
635 8 50       34 @keys or die 'DBIx::Abstract: insert must have fields';
636 8         21 $sql .= '(';
637 8         36 for ( my $i = 0 ; $i < @keys ; $i++ ) {
638 24 100       235 if ($i) { $sql .= ',' }
  16         26  
639 24         80 $sql .= ' ' . $keys[$i];
640             }
641 8         15 $sql .= ') VALUES (';
642 8         35 for ( my $i = 0 ; $i < @keys ; $i++ ) {
643 24 100       57 if ($i) { $sql .= ', ' }
  16         28  
644 24 50       58 if ( defined( $values[$i] ) ) {
645 24 50       80 if ( ref( $values[$i] ) eq 'SCALAR' ) {
    50          
646 0         0 $sql .= ${ $values[$i] };
  0         0  
647             }
648             elsif ( ref( $values[$i] ) eq 'ARRAY' ) {
649 0         0 $sql .= $values[$i][0];
650             }
651             else {
652 24         1449 $sql .= '?';
653 24         99 push( @bind_params, $values[$i] );
654             }
655             }
656             else {
657 0         0 $sql .= 'NULL';
658             }
659             }
660 8         33 $sql .= ')';
661             }
662             elsif ( !ref($fields) and $fields ) {
663 0         0 $sql .= $fields;
664             }
665             else {
666 0         0 die 'DBIx::Abstract: insert must have fields';
667             }
668 8         107 $self->__logwrite_sql( 1, $sql, @bind_params );
669 8         33 $self->__mod_query( $sql, @bind_params );
670 8         84 return $self;
671             }
672              
673             sub replace {
674 0     0 1 0 my $self = shift;
675 0         0 my ($table, $fields ) = @_;
676              
677             # $table == Name of table to update
678             # $fields == A reference to a hash of field/value pairs containing the
679             # new values for those fields.
680 0         0 my (@bind_params);
681 0 0       0 if ( ref($table) ) {
682 0         0 $fields = $table->{'fields'};
683 0         0 $table = $table->{'table'};
684             }
685              
686 0 0       0 $table or die 'DBIx::Abstract: insert must have table';
687              
688 0         0 my $sql = "REPLACE INTO $table ";
689 0 0 0     0 if ( ref($fields) eq 'HASH' ) {
    0          
690 0         0 my @keys = sort keys %$fields;
691 0         0 my @values = map {$fields->{$_}} @keys;
  0         0  
692 0 0       0 $#keys > -1 or die 'DBIx::Abstract: insert must have fields';
693 0         0 $sql .= '(';
694 0         0 for ( my $i = 0 ; $i <= $#keys ; $i++ ) {
695 0 0       0 if ($i) { $sql .= ',' }
  0         0  
696 0         0 $sql .= ' ' . $keys[$i];
697             }
698 0         0 $sql .= ') VALUES (';
699 0         0 for ( my $i = 0 ; $i <= $#keys ; $i++ ) {
700 0 0       0 if ($i) { $sql .= ', ' }
  0         0  
701 0 0       0 if ( defined( $values[$i] ) ) {
702 0 0       0 if ( ref( $values[$i] ) eq 'SCALAR' ) {
    0          
703 0         0 $sql .= ${ $values[$i] };
  0         0  
704             }
705             elsif ( ref( $values[$i] ) eq 'ARRAY' ) {
706 0         0 $sql .= $values[$i][0];
707             }
708             else {
709 0         0 $sql .= '?';
710 0         0 push( @bind_params, $values[$i] );
711             }
712             }
713             else {
714 0         0 $sql .= 'NULL';
715             }
716             }
717 0         0 $sql .= ')';
718             }
719             elsif ( !ref($fields) and $fields ) {
720 0         0 $sql .= $fields;
721             }
722             else {
723 0         0 die 'DBIx::Abstract: insert must have fields';
724             }
725 0         0 $self->__logwrite_sql( 1, $sql, @bind_params );
726 0         0 $self->__mod_query( $sql, @bind_params );
727 0         0 return $self;
728             }
729              
730             sub update {
731 1     1 1 649 my $self = shift;
732 1         4 my ($table, $fields, $where ) = @_;
733              
734             # $table == Name of table to update
735             # $fields == A reference to a hash of field/value pairs containing the
736             # new values for those fields.
737             # $where == One of my handy-dandy standard where's. See __where.
738 1         3 my ( $sql, @keys, @values, $i );
739 0         0 my (@bind_params);
740 1 50       6 if ( ref($table) ) {
741 0         0 $where = $table->{'where'};
742 0         0 $fields = $table->{'fields'};
743 0         0 $table = $table->{'table'};
744             }
745              
746             # "If you don't know what to do, don't do anything."
747             # -- St. O'Ffender, _Return of the Roller Blade Seven_
748 1 50       5 $table or die 'DBIx::Abstract: update must have table';
749              
750 1         4 $sql = "UPDATE $table SET";
751 1 50 0     6 if ( ref($fields) eq 'HASH' ) {
    0          
752 1         10 @keys = sort keys %$fields;
753 1         4 @values = map {$fields->{$_}} @keys;
  2         7  
754 1 50       6 $#keys > -1 or die 'DBIx::Abstract: update must have fields';
755 1         18 for ( $i = 0 ; $i <= $#keys ; $i++ ) {
756 2 100       6 if ($i) { $sql .= ',' }
  1         2  
757 2         7 $sql .= ' ' . $keys[$i] . '=';
758 2 50       7 if ( defined( $values[$i] ) ) {
759 2 50       5 if ( ref( $values[$i] ) eq 'SCALAR' ) {
760 0         0 $sql .= ${ $values[$i] };
  0         0  
761             }
762             else {
763 2         3 $sql .= '?';
764 2         10 push( @bind_params, $values[$i] );
765             }
766             }
767             else {
768 0         0 $sql .= 'NULL';
769             }
770             }
771             }
772             elsif ( !ref($fields) and $fields ) {
773 0         0 $sql .= " $fields";
774             }
775             else {
776 0         0 die 'DBIx::Abstract: update must have fields';
777             }
778              
779 1         7 my ( $moresql, @morebind ) = $self->__where($where);
780 1         2 $sql .= $moresql;
781 1         2 push( @bind_params, @morebind );
782              
783 1         6 $self->__logwrite_sql( 1, $sql, @bind_params );
784 1         5 $self->__mod_query( $sql, @bind_params );
785 1         14 return $self;
786             }
787              
788             sub select {
789 4     4 1 5262 my $self = shift;
790 4         14 my ( $fields, $table, $where, $order, $extra ) = @_;
791              
792             # $fields == A hash ref with the following values
793             # OR
794             # $fields == Fields to get data on, usually a *. (either scalar or
795             # array ref)
796             # $table == Name of table to update
797             # $where == One of my handy-dandy standard where's. See __where.
798             # $order == The order to output in
799 4         10 my $group; #== The key to group by, only available in hash mode
800 4         9 my ( $sql, $join );
801 4 100       19 if ( ref($fields) eq 'HASH' ) {
802 1         13 foreach ( sort keys %$fields ) {
803 5         9 my $field = lc $_;
804 5         6 $field =~ s/^-//;
805 5         10 $fields->{$field} = $fields->{$_};
806             }
807 1   33     9 $table = $fields->{'table'} || $fields->{'tables'};
808 1         3 $where = $fields->{'where'};
809 1         3 $order = $fields->{'order'};
810 1         2 $group = $fields->{'group'};
811 1         3 $extra = $fields->{'extra'};
812 1         3 $join = $fields->{'join'};
813              
814 1   33     5 $fields = $fields->{'fields'} || $fields->{'field'};
815             }
816 4         7 $sql = 'SELECT ';
817 4 50       15 if ( ref($fields) eq 'ARRAY' ) {
818 0         0 $sql .= join( ',', @$fields );
819             }
820             else {
821 4         9 $sql .= $fields;
822             }
823 4 50       12 if ( ref($table) eq 'ARRAY' ) {
824 0 0       0 if ( $#$table > -1 ) {
825 0         0 $sql .= ' FROM ' . join( ',', @$table );
826             }
827             }
828             else {
829 4 50       557 $sql .= " FROM $table" if $table;
830             }
831              
832 4         8 my ( $addsql, @bind_params );
833 4 50       10 if ( defined($where) ) {
834 4         21 ($addsql) = $self->__where( $where, 1 );
835 3 50       11 unless ($addsql) {
836 0         0 $where = undef;
837             }
838             }
839              
840 3 100       8 if ($join) {
841 1 50       7 unless ( ref($join) ) {
842 0         0 $join = [$join];
843             }
844 1 50       5 if ($where) {
845 1         4 $where = [$where];
846             }
847             else {
848 0         0 $where = [];
849             }
850 1         3 foreach ( @{$join} ) {
  1         3  
851 1 50       6 push( @$where, 'and' ) if $#$where > -1;
852 1         4 push( @$where, [$_] );
853             }
854             }
855              
856 3 50       12 if ( defined($where) ) {
857 3         7 ( $addsql, @bind_params ) = $self->__where($where);
858 3         8 $sql .= $addsql;
859             }
860              
861 3 50       15 if ( ref($group) eq 'ARRAY' ) {
    100          
862 0 0       0 if ( $#$group > -1 ) {
863 0         0 $sql .= ' GROUP BY ' . join( ',', @$group );
864             }
865             }
866             elsif ($group) {
867 1         3 $sql .= " GROUP BY $group";
868             }
869              
870 3 50       5591 if ( ref($order) eq 'ARRAY' ) {
    50          
871 0 0       0 if ( $#$order > -1 ) {
872 0         0 $sql .= ' ORDER BY ' . join( ',', @$order );
873             }
874             }
875             elsif ($order) {
876 0         0 $sql .= " ORDER BY $order";
877             }
878              
879 3 50       10 if ($extra) {
880 0         0 $sql .= ' ' . $extra;
881             }
882              
883 3         18 $self->__logwrite_sql( 2, $sql, @bind_params );
884 3         15 $self->__literal_query( $sql, @bind_params );
885 3         20 return $self;
886             }
887              
888             sub select_one_to_hashref {
889 0     0 1 0 my $self = shift;
890              
891             # Run a select and return a hash-ref of the first
892             # record returned from the select. Don't step
893             # on the current query, and don't keep the new
894             # one around.
895 0         0 my $db = $self->clone;
896 0         0 $self->__logwrite( 2, 'select_one_to_hashref' );
897 0         0 $db->select(@_);
898 0         0 my $result = $db->fetchrow_hashref;
899 0 0       0 return unless $result;
900 0         0 return {%$result};
901             }
902              
903             sub select_one_to_arrayref {
904 0     0 1 0 my $self = shift;
905 0         0 my $db = $self->clone;
906 0         0 $self->__logwrite( 2, 'select_one _to_arrayref' );
907 0         0 $db->select(@_);
908 0         0 my $result = $db->fetchrow_arrayref;
909 0 0       0 return unless $result;
910 0         0 return [@$result];
911             }
912              
913             sub select_one_to_array {
914 0     0 1 0 my $self = shift;
915 0         0 my $db = $self->clone;
916 0         0 $self->__logwrite( 2, 'select_one_to_arrayref' );
917 0         0 $db->select(@_);
918 0         0 my $result = $db->fetchrow_arrayref;
919 0 0       0 return unless $result;
920 0         0 return @$result;
921             }
922              
923             sub select_all_to_hashref {
924 0     0 1 0 my $self = shift;
925              
926             # Run a select and return a hash-ref.
927             # The hash-ref's key is the first
928             # field and it's value is the second.
929             # And it won't step on a current query.
930 0         0 my $db = $self->clone;
931 0         0 $self->__logwrite( 2, 'select_all_to_hash' );
932 0         0 $db->select(@_);
933 0         0 my $result = $db->fetchall_arrayref();
934 0 0       0 return unless $result;
935 0         0 my %to_ret;
936 0         0 foreach (@$result) {
937              
938 0 0       0 if ( $#$_ > 1 ) {
939 0         0 my $key = shift(@$_);
940 0         0 $to_ret{$key} = [@$_];
941             }
942             else {
943 0         0 $to_ret{ $_->[0] } = $_->[1];
944             }
945             }
946 0         0 $db = undef;
947 0         0 return {%to_ret};
948             }
949              
950             sub fetchrow_hashref {
951 0     0 1 0 my $self = shift;
952 0         0 $self->__logwrite( 4, 'fetchrow_hashref' );
953 0         0 my $row = $self->{'sth'}->fetchrow_hashref(@_);
954 0 0       0 unless ( defined($row) ) {
955 0         0 $self->{'sth'}->finish;
956             }
957 0         0 return $row;
958             }
959              
960             sub fetchrow_hash {
961 0     0 1 0 my $self = shift;
962 0         0 my $result = $self->fetchrow_hashref(@_);
963 0         0 $self->__logwrite( 4, 'fetchrow_hash' );
964 0 0       0 if ($result) {
965 0         0 return %$result;
966             }
967             else {
968 0         0 return ();
969             }
970             }
971              
972             sub fetchrow_arrayref {
973 0     0 0 0 my $self = shift;
974 0         0 $self->__logwrite( 4, 'fetchrow_arrayref' );
975 0         0 my $row = $self->{'sth'}->fetchrow_arrayref(@_);
976 0 0       0 unless ( defined($row) ) {
977 0         0 $self->{'sth'}->finish;
978             }
979 0         0 return $row;
980             }
981              
982             sub fetchrow_array {
983 7     7 1 35 my $self = shift;
984 7         17 $self->__logwrite( 4, 'fetchrow_array' );
985 7         102 my @row = $self->{'sth'}->fetchrow_array(@_);
986 7 100       21 if ( $#row == -1 ) {
987 2         12 $self->{'sth'}->finish;
988             }
989 7         28 return @row;
990             }
991              
992             sub fetchall_arrayref {
993 0     0 1 0 my $self = shift;
994 0         0 $self->__logwrite( 4, 'fetchall_arrayref' );
995 0         0 return $self->{'sth'}->fetchall_arrayref(@_);
996             }
997              
998             sub dataseek {
999 0     0 0 0 my $self = shift;
1000 0         0 my ($pos) = @_;
1001 0 0       0 if ( ref($pos) ) {
1002 0         0 $pos = $pos->{'pos'};
1003             }
1004 0 0 0     0 if ( $self->{'connect'}{'driver'} eq 'mysql'
1005             or $self->{'connect'}{'driver'} eq 'msql' )
1006             {
1007 0         0 return $self->{'sth'}->func( $pos, 'dataseek' );
1008             }
1009             else {
1010 0         0 die 'Dataseek is not supported by your database '
1011             . $self->{'connect'}{'driver'};
1012             }
1013             }
1014              
1015             sub rows {
1016 4     4 1 52 my $self = shift;
1017 4         20 $self->__logwrite( 5, 'rows' );
1018 4         46 return $self->{'sth'}->rows;
1019             }
1020              
1021             sub errstr {
1022 0     0 0 0 my $class = shift;
1023 0         0 my $self;
1024 0 0       0 if ( ref($class) ) { $self = $class }
  0         0  
1025 0 0 0     0 if ( $self and $self->{'dbh'} ) {
1026 0         0 return $self->{'dbh'}->errstr;
1027             }
1028             else {
1029 0         0 return $DBI::errstr;
1030             }
1031             }
1032              
1033             sub err {
1034 0     0 0 0 my $class = shift;
1035 0         0 my $self;
1036 0 0       0 if ( ref($class) ) { $self = $class }
  0         0  
1037 0 0 0     0 if ( $self and $self->{'dbh'} ) {
1038 0         0 return $self->{'dbh'}->err;
1039             }
1040             else {
1041 0         0 return $DBI::err;
1042             }
1043             }
1044              
1045             #### Mysql compatibility functions
1046             #### These are not documented, and shouldn't be.
1047             #### They are here to make it easier for lazy people
1048             #### to switch.
1049             #### These may get warnings associated with them.
1050             #### These may go away.
1051              
1052             sub fetchrow {
1053 0     0 0 0 my $self = shift;
1054 0         0 return $self->fetchrow_array(@_);
1055             }
1056              
1057             sub fetchhash {
1058 0     0 0 0 my $self = shift;
1059 0         0 return $self->fetchrow_hash(@_);
1060             }
1061              
1062             sub numrows {
1063 0     0 0 0 my $self = shift;
1064 0         0 return $self->rows(@_);
1065             }
1066              
1067             sub quote {
1068 33     33 1 51 my $self = shift;
1069 33         326 $self->{'dbh'}->quote(@_);
1070             }
1071              
1072             sub disconnect {
1073 4     4 1 1660 my $self = shift;
1074 4         11 $self->{'Active'} = 0;
1075 4         266 return $self->{'dbh'}->disconnect();
1076             }
1077              
1078             sub AUTOLOAD {
1079             ### This will delegate calls for selected methods from the DBH and STH
1080             ### objects. This allows users limited access to their functionality.
1081 0     0     my $self = shift;
1082              
1083             # $self == 'Class=REFERENCE'
1084 0           my ($class) = split( /=/, $self );
1085              
1086             # $AUTOLOAD == 'Class::method'
1087 0           my $method = $AUTOLOAD;
1088 0           my $sr = '^' . $class . '::';
1089 0           $method =~ s/$sr//;
1090              
1091             # These are just space separated lists of methods that may be passed
1092             # through to the dbh or sth objects respectively.
1093             #
1094             # If anything ends up in here we should probably make a separate function
1095             # for it (if only to keep the logging working properly).
1096 0           my $DBHVALIDMETHODS = 'commit ' . 'rollback ' . 'trace';
1097 0           my $STHVALIDMETHODS = 'finish ' . 'bind_col ' . 'bind_columns';
1098              
1099             # If this is a dbh method, pass it through
1100 0 0         if ( $DBHVALIDMETHODS =~ /\b$method\b/ ) {
    0          
1101 0           $self->__logwrite( 5, "dbh->$method" );
1102 0 0         return $self->{'dbh'}->$method(@_) if $self->{'dbh'};
1103              
1104             # If this is an sth method, pass it through
1105             }
1106             elsif ( $STHVALIDMETHODS =~ /\b$method\b/ ) {
1107 0           $self->__logwrite( 5, "sth->$method" );
1108 0 0         return $self->{'sth'}->$method(@_) if $self->{'sth'};
1109             }
1110             else {
1111 0           $self->__logwrite( 0, "Unknown method: class=$class method=$method" );
1112 0           die "($$)Unknown method: class=$class method=$method\n";
1113             }
1114             }
1115              
1116             1;
1117              
1118             __END__