File Coverage

blib/lib/DBIx/Broker.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package DBIx::Broker;
2              
3             #
4             # DBIx::Broker
5             #
6             # This Perl module provides a cleaner and more manageable API
7             # to use for interaction with DBI-compatible databases. Queries,
8             # updates, inserts, counts, deletions, and even arbitrary SQL
9             # statements can be executed in one line of top-level code, rather
10             # than the usual three or four that contain that ugly ->prepare()
11             # and ->execute() clutter. SELECT row results may be retrieved
12             # either all at once (returned as an array of hash/arrayrefs,
13             # unless only one column's values are desired) or incrementally (a
14             # hash/arrayref at a time). It is also possible to print debugging
15             # messages (the raw SQL statements) to any valid handle.
16             #
17             # This module is released under the GPL, which means that you are
18             # not only allowed, but encouraged to modify it to suit your needs
19             # and submit enhancements and modifications to the author listed
20             # below. Thank you!
21             #
22              
23             # Copyright (C) 2000 xomina@bitstream.net
24              
25             # This program is free software; you can redistribute it and/or
26             # modify it under the terms of the GNU General Public License
27             # as published by the Free Software Foundation; either version 2
28             # of the License, or (at your option) any later version.
29              
30             # This program is distributed in the hope that it will be useful,
31             # but WITHOUT ANY WARRANTY; without even the implied warranty of
32             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33             # GNU General Public License for more details.
34              
35             # You should have received a copy of the GNU General Public License
36             # along with this program, in a file called 'LICENSE'; if not, write
37             # to
38             #
39             # Free Software Foundation, Inc.
40             # 59 Temple Place - Suite 330
41             # Boston, MA 02111-1307
42             # USA.
43              
44              
45              
46 1     1   1515 use DBI; # ...you've got the magic!
  0            
  0            
47             use Carp;
48             use strict;
49              
50             BEGIN {
51             require Exporter;
52             use vars qw( $VERSION @ISA );
53             $VERSION = (split ' ', q!$Revision: 1.14 $!)[1];
54             @ISA = qw( Exporter );
55             }
56              
57              
58             sub new {
59             my $class = shift;
60             my ( $self );
61              
62             $self = { };
63              
64             if ( @_ ) {
65             my ( $driver, $database, $hostname, $port, $user, $password ) = @_;
66             if ( $driver eq 'mysql' ) {
67             $self->{'data_source'} = "DBI:$driver:$database:$hostname:$port";
68             }
69             else {
70             $self->{'data_source'} = "DBI:$driver:$database";
71             }
72              
73             $self->{'db'} = DBI->connect( $self->{'data_source'},
74             $user, $password );
75             if ( ! $self->{'db'} ) {
76             die "Could not open database $database!\n\n";
77             }
78             $self->{'driver'} = $driver;
79             $self->{'database'} = $database;
80             $self->{'hostname'} = $hostname;
81             $self->{'port'} = $port;
82             $self->{'user'} = $user;
83             $self->{'password'} = $password;
84              
85             $self->{'force_lowercase'} = 0;
86             }
87              
88             bless( $self, $class );
89             }
90              
91              
92             sub clone {
93             my $self = shift;
94             my ( $class, $clone );
95              
96             if ( $class = ref($self) ) {
97             $clone = { };
98             $clone->{'db'} = DBI->connect( $self->{'data_source'},
99             $self->{'user'}, $self->{'password'} );
100             if ( ! $clone->{'db'} ) {
101             die "Could not open database $self->{'database'}!\n\n";
102             }
103             $clone->{'driver'} = $self->{'driver'};
104             $clone->{'database'} = $self->{'database'};
105             $clone->{'hostname'} = $self->{'hostname'};
106             $clone->{'port'} = $self->{'port'};
107             $clone->{'user'} = $self->{'user'};
108             $clone->{'password'} = $self->{'password'};
109             }
110             else {
111             # ...and what would you be doing in here?
112             die "\nYou cannot clone an object before one is created! Use new() first.\n\n";
113             }
114             bless( $clone, $class );
115             }
116              
117              
118             sub set_db_handle { # for use with programs which have already connected to the db
119             my $self = shift;
120              
121             $self->{'db'} = shift;
122             }
123              
124              
125             sub get_db_handle {
126             my $self = shift;
127              
128             return $self->{'db'};
129             }
130              
131              
132             sub debug_on { # call it like this, e.g.: $db->debug_on( \*STDERR );
133             my $self = shift;
134             my $debug_handle = shift;
135              
136             # we check to see if it's valid before agreeing to use it
137             if ( (ref($debug_handle) eq 'Fh') || (ref($debug_handle) eq 'GLOB') ) {
138             $self->{'debug'} = 1;
139             $self->{'debug_handle'} = $debug_handle;
140             }
141             }
142              
143              
144             sub debug_off {
145             my $self = shift;
146              
147             $self->{'debug'} = 0;
148             undef( $self->{'debug_handle'} );
149             }
150              
151              
152             sub force_lowercase_fields {
153             my $self = shift;
154              
155             $self->{'force_lowercase'} = 1;
156             }
157              
158              
159             sub use_db { # for some reason there's no built-in DBI method for this..
160             my $self = shift;
161             my $database = shift;
162              
163             return if ( $self->{'driver'} ne 'mysql' );
164              
165             $self->{'sql'} = "USE $database";
166              
167             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
168              
169             $self->__sql_execute();
170              
171             $self->{'database'} = $database;
172             }
173              
174              
175             sub finish {
176             my $self = shift;
177              
178             $self->{'statement'}->finish();
179             }
180              
181              
182             sub disconnect {
183             my $self = shift;
184              
185             $self->{'db'}->disconnect();
186             }
187              
188              
189             sub is_active {
190             my $self = shift;
191              
192             return if ( $self->{'driver'} ne 'mysql' );
193              
194             return $self->{'db'}->{'Active'};
195             }
196              
197              
198             sub select {
199             my $self = shift;
200             my $fields = shift;
201             my $tables = shift;
202             my $stipulations = shift;
203             my $wants_a_hash = shift;
204             my ( @fields, @tables );
205              
206             $self->__add_to_array( $fields, \@fields );
207             $self->__add_to_array( $tables, \@tables );
208              
209             if ( ! defined($self->{'db'}) ) {
210             die "There is not a valid DB handle from which to select data.\n";
211             }
212            
213             local $" = ", ";
214             $self->{'sql'} = "SELECT @fields FROM @tables ";
215             $self->{'sql'} .= $stipulations if $stipulations;
216              
217             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
218              
219             $self->__sql_execute( $wants_a_hash );
220             }
221              
222              
223             sub select_one_value {
224             my $self = shift;
225             my $field = shift;
226             my $tables = shift;
227             my $stipulations = shift;
228             my ( @tables );
229              
230             $self->__add_to_array( $tables, \@tables );
231              
232             local $" = ', ';
233             $self->{'sql'} = "SELECT $field FROM @tables ";
234             $self->{'sql'} .= $stipulations if $stipulations;
235              
236             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
237              
238             my @query_results = $self->__sql_execute();
239             return $query_results[0]->[0];
240             }
241              
242              
243             sub select_one_row {
244             my $self = shift;
245             my $fields = shift;
246             my $tables = shift;
247             my $stipulations = shift;
248             my $wants_a_hash = shift;
249             my ( @fields, @tables );
250              
251             $self->__add_to_array( $fields, \@fields );
252             $self->__add_to_array( $tables, \@tables );
253              
254             local $" = ", ";
255             $self->{'sql'} = "SELECT @fields FROM @tables ";
256             $self->{'sql'} .= $stipulations if $stipulations;
257              
258             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
259              
260             my @query_results = $self->__sql_execute( $wants_a_hash );
261             return $query_results[0];
262             }
263              
264              
265             sub select_one_column {
266             my $self = shift;
267             my $field = shift;
268             my $tables = shift;
269             my $stipulations = shift;
270             my ( @tables );
271              
272             $self->__add_to_array( $tables, \@tables );
273              
274             local $" = ", ";
275             $self->{'sql'} = "SELECT $field FROM @tables ";
276             $self->{'sql'} .= $stipulations if $stipulations;
277              
278             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
279              
280             my @query_results = $self->__sql_execute( 1 ); # TODO: find out why this didn't work with arrayrefs!!
281             my ( $result_data, @array_of_scalars );
282             $field =~ s/^\s*distinct\s+(\S+)/$1/i;
283             $field =~ s/.*\.(.*)/$1/;
284             foreach $result_data ( @query_results ) {
285             push( @array_of_scalars, $result_data->{$field} );
286             }
287             return @array_of_scalars;
288             }
289              
290              
291             sub select_all {
292             my $self = shift;
293             my $tables = shift;
294             my $stipulations = shift;
295             my $wants_a_hash = shift;
296             my ( @tables );
297              
298             $self->__add_to_array( $tables, \@tables );
299              
300             $self->select( [ "*" ], \@tables, $stipulations, $wants_a_hash );
301             }
302              
303              
304             sub select_incrementally {
305             my $self = shift;
306             my $fields = shift;
307             my $tables = shift;
308             my $stipulations = shift;
309             my ( @fields, @tables );
310              
311             $self->__add_to_array( $fields, \@fields );
312             $self->__add_to_array( $tables, \@tables );
313              
314             if ( ! defined($self->{'db'}) ) {
315             die "There is not a valid DB handle from which to select data.\n";
316             }
317              
318             local $" = ", ";
319             $self->{'sql'} = "SELECT @fields FROM @tables ";
320             $self->{'sql'} .= $stipulations if $stipulations;
321              
322             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
323              
324             $self->{'statement'} = $self->{'db'}->prepare( $self->{'sql'} );
325             if ( ! defined $self->{'statement'} ) {
326             die "Cannot prepare statement (error ".$self->{'db'}->err."): ".$self->{'db'}->errstr."\n";
327             }
328             $self->{'statement'}->execute();
329             # we now leave the $self->{'statement'} object "hanging open" for use by &get_next_row()
330             }
331              
332              
333             sub select_all_incrementally {
334             my $self = shift;
335             my $tables = shift;
336             my $stipulations = shift;
337             my ( @tables );
338              
339             $self->__add_to_array( $tables, \@tables );
340              
341             $self->select_incrementally( [ "*" ], \@tables, $stipulations );
342             }
343              
344              
345             sub get_next_row {
346             my $self = shift;
347             my $wants_a_hash = shift;
348            
349             if ( ! defined($self->{'db'}) ) {
350             die "There is not a valid DB handle from which to select another row of results.\n";
351             }
352            
353             if ( $wants_a_hash ) {
354             return $self->{'statement'}->fetchrow_hashref();
355             }
356             else {
357             return $self->{'statement'}->fetchrow_arrayref();
358             } # i personally have no use for fetchrow[_array]... ( does anybody? )
359             }
360              
361              
362             sub rows {
363             my $self = shift;
364              
365             if ( $self->{'statement'} ) {
366             return $self->{'statement'}->rows();
367             }
368             else {
369             return 0;
370             }
371             }
372              
373              
374             sub count {
375             my $self = shift;
376             my $tables = shift;
377             my $stipulations = shift;
378             my ( @tables );
379              
380             $self->__add_to_array( $tables, \@tables );
381              
382             if ( ! defined($self->{'db'}) ) {
383             die "There is not a valid DB handle from which to select a count.\n";
384             }
385              
386             local $" = ", ";
387             $self->{'sql'} = "SELECT COUNT(*) FROM @tables $stipulations";
388              
389             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
390              
391             my @query_results = $self->__sql_execute();
392             return $query_results[0]->[0];
393             }
394              
395              
396             sub insert {
397             my $self = shift;
398             my $table = shift; # send only one table
399             my %new_data = %{ shift @_ };
400             my @fields = keys( %new_data ); # these are promised to be in the same order,
401             my @values = values( %new_data ); # according to the docs
402             my @question_marks = ( "?" ) x @values;
403            
404             if ( ! defined($self->{'db'}) ) {
405             die "There is not a valid DB handle into which to insert data.\n\n";
406             }
407            
408             local $" = ", ";
409             $self->{'sql'} = "INSERT INTO $table ( @fields ) VALUES ( @question_marks )";
410              
411             print { $self->{'debug_handle'} } "sql: $self->{'sql'}\nvalues: @values\n" if $self->{'debug'};
412              
413             $self->__sql_execute( \@values );
414             return $self->{'statement'}->{'mysql_insertid'} if ( $self->{'driver'} eq 'mysql' );
415             }
416              
417              
418             sub update {
419             my $self = shift;
420             my $table = shift; # send only one table
421             my %new_data = %{ shift @_ };
422             my $stipulations = shift;
423             my @fields = keys( %new_data ); # these are promised to be in the same order,
424             my @values = values( %new_data ); # according to the docs
425            
426             if ( ! defined($self->{'db'}) ) {
427             die "There is not a valid DB handle to update.\n";
428             }
429            
430             $self->{'sql'} = "UPDATE $table SET ";
431             foreach ( @fields ) {
432             $self->{'sql'} .= "$_ = ?, ";
433             }
434             $self->{'sql'} =~ s/\,\s$/ /; # chop off the last comma
435             $self->{'sql'} .= $stipulations if $stipulations;
436            
437             print { $self->{'debug_handle'} } "sql: $self->{'sql'}\nvalues: @values\n" if $self->{'debug'};
438              
439             $self->__sql_execute( \@values );
440             }
441              
442              
443             sub delete {
444             my $self = shift;
445             my $table = shift;
446             my $stipulations = shift;
447            
448             if ( ! defined($self->{'db'}) ) {
449             die "There is not a valid DB handle from which to delete data.\n";
450             }
451              
452             $self->{'sql'} = "DELETE FROM $table $stipulations";
453              
454             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
455            
456             $self->__sql_execute();
457             }
458              
459              
460             sub delete_all {
461             my $self = shift;
462             my $table = shift;
463            
464             if ( ! defined($self->{'db'}) ) {
465             die "There is not a valid DB handle from which to delete data.\n";
466             }
467            
468             $self->{'sql'} = "DELETE FROM $table";
469             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
470            
471             $self->__sql_execute();
472             }
473              
474              
475             sub get_table_schema {
476             my $self = shift;
477             my $table = shift;
478              
479             $self->{'sql'} = "SHOW COLUMNS FROM $table";
480              
481             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
482              
483             $self->{'statement'} = $self->{'db'}->prepare( $self->{'sql'} );
484             if ( ! defined $self->{'statement'} ) {
485             die "Cannot prepare statement (error ".$self->{'db'}->err."): ".$self->{'db'}->errstr."\n";
486             }
487             $self->{'statement'}->execute();
488              
489             my ( %table_schema, $column_info, $field_name );
490             while ( $column_info = $self->{'statement'}->fetchrow_hashref() ) {
491             $field_name = delete( $column_info->{'Field'} );
492             $table_schema{$field_name} = $column_info;
493             }
494             return %table_schema;
495             }
496              
497              
498             sub get_primary_key {
499             my $self = shift;
500             my $table = shift;
501              
502             my @keys;
503             my %table_schema = $self->get_table_schema( $table );
504              
505             foreach ( keys(%table_schema) ) {
506             push( @keys, $_ ) if ( $table_schema{$_}->{'Key'} =~ /pri/i );
507             }
508             return wantarray ? @keys : $keys[0];
509             }
510              
511              
512             sub get_auto_increments {
513             my $self = shift;
514             my $table = shift;
515             my ( %table_schema, @auto_increments );
516              
517             %table_schema = $self->get_table_schema( $table );
518             foreach ( keys(%table_schema) ) {
519             if ( $table_schema{$_}->{'Extra'} =~ /auto_increment/i ) {
520             push( @auto_increments, $_ );
521             }
522             }
523             return @auto_increments;
524             }
525              
526              
527             # this is merely a convenience wrapper method..
528             sub func {
529             my $self = shift;
530              
531             # the last parameter is the function name ( e.g., '_ListTables' )
532             my $func_name = pop @_;
533              
534             # anything else is the initial argument list
535             my @func_arguments = @_;
536              
537             $self->{'db'}->func( @func_arguments, $func_name );
538             }
539              
540              
541             sub execute_sql {
542             my $self = shift;
543             $self->{'sql'} = shift;
544             my $wants_a_hash = shift;
545              
546             print { $self->{'debug_handle'} } "$self->{'sql'}\n" if $self->{'debug'};
547              
548             $self->__sql_execute( $wants_a_hash );
549             return $self->{'statement'}->{'mysql_insertid'} if ( $self->{'driver'} eq 'mysql' );
550             }
551              
552              
553             sub __sql_execute {
554             my $self = shift;
555             my ( @values, $wants_a_hash );
556            
557             if ( @_ eq 2 ) {
558             @values = @{ shift @_ }; # the @values fill in the ?s
559             $wants_a_hash = shift @_;
560             }
561             else {
562             if ( ref($_[0]) eq 'ARRAY' ) {
563             @values = @{ shift @_ };
564             }
565             else {
566             $wants_a_hash = shift @_;
567             }
568             }
569            
570             $self->{'statement'} = $self->{'db'}->prepare( $self->{'sql'} );
571             if ( ! defined $self->{'statement'} || $DBI::err ) {
572             die "Cannot prepare statement (error ".$self->{'db'}->err."): ".$self->{'db'}->errstr."\n";
573             }
574            
575             $self->{'statement'}->execute( @values ); # ignores @values if undefined ( right? )
576            
577             if ( $DBI::err ) {
578             die "Cannot execute statement (error ".$self->{'db'}->err."): ".$self->{'db'}->errstr."\n";
579             }
580              
581             my @results;
582            
583             # the following if() strikes me as really stupid, as i must avoid the block
584             # so that the fetchrow calls do not error for non-SELECT statements...
585             # best to me would be for fetchrow to quietly do nothing if there are
586             # no rows... but hey.
587            
588             if ( $self->{'statement'}->{'NUM_OF_FIELDS'} ) {
589             if ( $wants_a_hash ) {
590             my $hash_ref;
591             while ( $hash_ref = $self->{'statement'}->fetchrow_hashref() ) {
592             if ( $self->{'force_lowercase'} ) {
593             my %lc_hash;
594             @lc_hash{ map { lc($_) } keys(%{$hash_ref}) } = values(%{$hash_ref});
595             $hash_ref = \%lc_hash;
596             }
597             push @results, $hash_ref;
598             }
599             } else {
600             my $array_ref;
601             while ( $array_ref = $self->{'statement'}->fetchrow_arrayref() ) {
602             push @results, $array_ref;
603             }
604             }
605             }
606             return @results;
607             }
608              
609              
610             sub __add_to_array {
611             my $self = shift;
612             my $array_or_not = shift;
613             my $target_array = shift;
614              
615             if ( ref($array_or_not) eq 'ARRAY' ) {
616             push( @{$target_array}, @{$array_or_not} );
617             }
618             else {
619             push( @{$target_array}, $array_or_not );
620             }
621             }
622              
623              
624             1;
625              
626             __END__