File Coverage

blib/lib/DBIx/FlexibleBinding.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1 1     1   47959 use strict;
  1         2  
  1         22  
2 1     1   4 use warnings;
  1         2  
  1         25  
3 1     1   756 use MRO::Compat 'c3';
  1         3176  
  1         43  
4              
5             package DBIx::FlexibleBinding;
6             our $VERSION = '2.0.3'; # VERSION
7             # ABSTRACT: Greater statement placeholder and data-binding flexibility.
8 1     1   2181 use DBI ();
  1         18538  
  1         28  
9 1     1   8 use Exporter ();
  1         1  
  1         15  
10 1     1   1136 use Message::String ();
  0            
  0            
11             use Scalar::Util ( 'reftype', 'blessed', 'weaken' );
12             use Sub::Util ( 'set_subname' );
13             use namespace::clean;
14             use Params::Callbacks ( 'callback' );
15             use message << 'EOF';
16             CRIT_EXP_AREF_AFTER Expected a reference to an ARRAY after argument (%s)
17             CRIT_UNEXP_ARG Unexpected argument (%s)
18             CRIT_EXP_SUB_NAMES Expected a sub name, or reference to an array of sub names
19             CRIT_EXP_HANDLE Expected a %s::database or statement handle
20             CRIT_DBI A DBI error occurred\n%s
21             CRIT_PROXY_UNDEF Handle (%s) undefined
22             EOF
23              
24             our @ISA = qw(DBI Exporter);
25             our @EXPORT = qw(callback);
26             our $AUTO_BINDING_ENABLED = 1;
27              
28             sub _is_arrayref
29             {
30             return ref( $_[0] ) && reftype( $_[0] ) eq 'ARRAY';
31             }
32              
33             sub _is_hashref
34             {
35             return ref( $_[0] ) && reftype( $_[0] ) eq 'HASH';
36             }
37              
38             sub _as_list_or_ref
39             {
40             return wantarray ? @{ $_[0] } : $_[0]
41             if defined $_[0];
42             return wantarray ? () : undef;
43             }
44              
45             sub _create_namespace_alias
46             {
47             my ( $package, $ns_alias ) = @_;
48             return $package unless $ns_alias;
49             no strict 'refs';
50             for ( '', 'db', 'st' ) {
51             my $ext .= $_ ? "\::$_" : $_;
52             $ext .= '::';
53             *{ $ns_alias . $ext } = *{ $package . $ext };
54             }
55             return $package;
56             }
57              
58             sub _create_dbi_handle_proxies
59             {
60             my ( $package, $caller, $list_of_sub_names ) = @_;
61             return $package unless $list_of_sub_names;
62             if ( ref $list_of_sub_names ) {
63             CRIT_EXP_SUB_NAMES
64             unless _is_arrayref( $list_of_sub_names );
65             for my $sub_name ( @$list_of_sub_names ) {
66             $package->_create_dbi_handle_proxy( $caller, $sub_name );
67             }
68             }
69             else {
70             $package->_create_dbi_handle_proxy( $caller, $list_of_sub_names );
71             }
72             return $package;
73             }
74              
75             our %proxies;
76              
77             sub _create_dbi_handle_proxy
78             {
79             # A DBI Handle Proxy is a subroutine masquerading as a database or
80             # statement handle object in the calling package namespace. They're
81             # intended to function as a pure convenience if that convenience is
82             # wanted.
83             my ( $package, $caller, $sub_name ) = @_;
84             my $fqpi = "$caller\::$sub_name";
85             no strict 'refs';
86             *$fqpi = set_subname(
87             $sub_name => sub {
88             unshift @_, ( $package, $fqpi );
89             goto &_service_call_to_a_dbi_handle_proxy;
90             }
91             );
92             $proxies{$fqpi} = undef;
93             return $package;
94             }
95              
96             sub _service_call_to_a_dbi_handle_proxy
97             {
98             # This is the handler servicing calls to a DBI Handle Proxy. It
99             # imparts a set of overloaded behaviours on the object, each
100             # triggered by a different usage context.
101             my ( $package, $fqpi, @args ) = @_;
102             return $proxies{$fqpi}
103             unless @args;
104             if ( @args == 1 ) {
105             unless ( $args[0] ) {
106             undef $proxies{$fqpi};
107             return $proxies{$fqpi};
108             }
109             if ( blessed( $args[0] ) ) {
110             if ( $args[0]->isa( "$package\::db" ) ) {
111             weaken( $proxies{$fqpi} = $args[0] );
112             }
113             elsif ( $args[0]->isa( "$package\::st" ) ) {
114             weaken( $proxies{$fqpi} = $args[0] );
115             }
116             else {
117             CRIT_EXP_HANDLE( $package );
118             }
119             return $proxies{$fqpi};
120             }
121             }
122             if ( $args[0] =~ m{^dbi:}i ) {
123             $proxies{$fqpi} = $package->connect( @args )
124             or CRIT_DBI( $DBI::errstr );
125             }
126             else {
127             if ( $proxies{$fqpi} ) {
128             my $proxy = $proxies{$fqpi};
129             $proxy->execute( @args )
130             if $proxy->isa( "$package\::st" );
131             return $proxy->getrows( @args );
132             }
133             else {
134             CRIT_PROXY_UNDEF( $fqpi );
135             }
136             }
137             return $proxies{$fqpi};
138             } ## end sub _service_call_to_a_dbi_handle_proxy
139              
140             sub import
141             {
142             my ( $package, @args ) = @_;
143             my $caller = caller;
144             @_ = ( $package );
145              
146             while ( @args ) {
147             my $arg = shift( @args );
148              
149             if ( substr( $arg, 0, 1 ) eq '-' ) {
150             if ( $arg eq '-alias' || $arg eq '-as' ) {
151             my $ns_alias = shift @args;
152             $package->_create_namespace_alias( $ns_alias );
153             }
154             elsif ( $arg eq '-subs' ) {
155             my $list_of_sub_names = shift @args;
156             $package->_create_dbi_handle_proxies( $caller, $list_of_sub_names );
157             }
158             else {
159             CRIT_UNEXP_ARG( $arg );
160             }
161             }
162             else {
163             push @_, $arg;
164             }
165             }
166              
167             goto &Exporter::import;
168             }
169              
170             sub connect
171             {
172             my ( $invocant, $dsn, $user, $pass, $attr ) = @_;
173             $attr = {}
174             unless defined $attr;
175             $attr->{RootClass} = ref( $invocant ) || $invocant
176             unless defined $attr->{RootClass};
177             return $invocant->next::method( $dsn, $user, $pass, $attr );
178             }
179              
180             package # Hide from PAUSE
181             DBIx::FlexibleBinding::db;
182             our $VERSION = '2.0.3'; # VERSION
183              
184             BEGIN {
185             *_is_hashref = \&DBIx::FlexibleBinding::_is_hashref;
186             *_as_list_or_ref = \&DBIx::FlexibleBinding::_as_list_or_ref;
187             }
188              
189             use Params::Callbacks 'callbacks';
190             use namespace::clean;
191              
192             our @ISA = 'DBI::db';
193              
194             sub do
195             {
196             my ( $callbacks, $dbh, $sth, @bind_values ) = &callbacks;
197             my $result;
198             unless ( ref $sth ) {
199             my $attr;
200             $attr = shift @bind_values
201             if _is_hashref( $bind_values[0] );
202             $sth = $dbh->prepare( $sth, $attr );
203             return undef
204             if $sth->err;
205             }
206             $result = $sth->execute( @bind_values );
207             return undef
208             if $sth->err;
209             local $_;
210             $result = $callbacks->smart_transform( $_ = $result )
211             if @$callbacks;
212             return $result;
213             }
214              
215             sub prepare
216             {
217             my ( $dbh, $stmt, @args ) = @_;
218             my @params;
219             if ( $stmt =~ m{:\w+\b} ) {
220             @params = $stmt =~ m{:(\w+)\b}g;
221             $stmt =~ s{:\w+\b}{?}g;
222             }
223             elsif ( $stmt =~ m{\@\w+\b} ) {
224             @params = $stmt =~ m{(\@\w+)\b}g;
225             $stmt =~ s{\@\w+\b}{?}g;
226             }
227             elsif ( $stmt =~ m{\?\d+\b} ) {
228             @params = $stmt =~ m{\?(\d+)\b}g;
229             $stmt =~ s{\?\d+\b}{?}g;
230             }
231             else {
232             # No recognisable placeholders to extract/convert
233             }
234             my $sth = $dbh->next::method( $stmt, @args );
235             return $sth
236             unless defined $sth;
237             $sth->_init_private_attributes( \@params );
238             return $sth;
239             }
240              
241             sub getrows_arrayref
242             {
243             my ( $callbacks, $dbh, $sth, @bind_values ) = &callbacks;
244             unless ( ref $sth ) {
245             my $attr;
246             $attr = shift( @bind_values )
247             if _is_hashref( $bind_values[0] );
248             $sth = $dbh->prepare( $sth, $attr );
249             return _as_list_or_ref( undef )
250             if $sth->err;
251             }
252             $sth->execute( @bind_values );
253             return _as_list_or_ref( undef )
254             if $sth->err;
255             return $sth->getrows_arrayref( $callbacks );
256             }
257              
258             sub getrows_hashref
259             {
260             my ( $callbacks, $dbh, $sth, @bind_values ) = &callbacks;
261             unless ( ref $sth ) {
262             my $attr;
263             $attr = shift( @bind_values )
264             if _is_hashref( $bind_values[0] );
265             $sth = $dbh->prepare( $sth, $attr );
266             return _as_list_or_ref( undef )
267             if $sth->err;
268             }
269             $sth->execute( @bind_values );
270             return _as_list_or_ref( undef )
271             if $sth->err;
272             return $sth->getrows_hashref( $callbacks );
273             }
274              
275             sub getrow_arrayref
276             {
277             my ( $callbacks, $dbh, $sth, @bind_values ) = &callbacks;
278             unless ( ref $sth ) {
279             my $attr;
280             $attr = shift( @bind_values )
281             if _is_hashref( $bind_values[0] );
282             $sth = $dbh->prepare( $sth, $attr );
283             return undef
284             if $sth->err;
285             }
286              
287             $sth->execute( @bind_values );
288             return undef
289             if $sth->err;
290             return $sth->getrow_arrayref( $callbacks );
291             }
292              
293             sub getrow_hashref
294             {
295             my ( $callbacks, $dbh, $sth, @bind_values ) = &callbacks;
296             unless ( ref $sth ) {
297             my $attr;
298             $attr = shift( @bind_values )
299             if _is_hashref( $bind_values[0] );
300             $sth = $dbh->prepare( $sth, $attr );
301             return undef
302             if $sth->err;
303             }
304             $sth->execute( @bind_values );
305             return undef
306             if $sth->err;
307             return $sth->getrow_hashref( $callbacks );
308             }
309              
310             BEGIN {
311             *getrows = \&getrows_hashref;
312             *getrow = \&getrow_hashref;
313             }
314              
315             package # Hide from PAUSE
316             DBIx::FlexibleBinding::st;
317             our $VERSION = '2.0.3'; # VERSION
318              
319             BEGIN {
320             *_is_arrayref = \&DBIx::FlexibleBinding::_is_arrayref;
321             *_is_hashref = \&DBIx::FlexibleBinding::_is_hashref;
322             *_as_list_or_ref = \&DBIx::FlexibleBinding::_as_list_or_ref;
323             }
324              
325             use List::MoreUtils ( 'any' );
326             use Params::Callbacks ( 'callbacks' );
327             use namespace::clean;
328             use message << 'EOF';
329             ERR_EXP_AHR Expected a reference to a HASH or ARRAY
330             ERR_MISSING_BIND_ID Binding identifier is missing
331             ERR_BAD_BIND_ID Bad binding identifier (%s)
332             EOF
333              
334             our @ISA = 'DBI::st';
335              
336             sub _priv_attr
337             {
338             my ( $sth, $name, $value, @more ) = @_;
339             return $sth->{private_dbix_flexbind}
340             unless @_ > 1;
341             $sth->{private_dbix_flexbind}{$name} = $value
342             if @_ > 2;
343             return $sth->{private_dbix_flexbind}{$name}
344             unless @more;
345             while ( @more ) {
346             $name = shift @more;
347             $sth->{private_dbix_flexbind}{$name} = shift @more;
348             }
349             return $sth;
350             }
351              
352             sub _auto_bind
353             {
354             my ( $sth, $value ) = @_;
355             return $sth->_priv_attr( 'auto_bind' )
356             unless @_ > 1;
357             $sth->_priv_attr( auto_bind => !!$value );
358             return $sth;
359             }
360              
361             sub _param_count
362             {
363             my ( $sth, $value ) = @_;
364             if ( @_ > 1 ) {
365             $sth->_priv_attr( 'param_count' => $value );
366             return $sth;
367             }
368             $sth->_priv_attr( 'param_count' => {} )
369             unless defined $sth->_priv_attr( 'param_count' );
370             return %{ $sth->_priv_attr( 'param_count' ) }
371             if wantarray;
372             return $sth->_priv_attr( 'param_count' );
373             }
374              
375             sub _param_order
376             {
377             my ( $sth, $value ) = @_;
378             if ( @_ > 1 ) {
379             $sth->_priv_attr( param_order => $value );
380             return $sth;
381             }
382             $sth->_priv_attr( param_order => [] )
383             unless defined $sth->_priv_attr( 'param_order' );
384             return @{ $sth->_priv_attr( 'param_order' ) }
385             if wantarray;
386             return $sth->_priv_attr( 'param_order' );
387             }
388              
389             sub _using_named
390             {
391             my ( $sth, $value ) = @_;
392             return $sth->_priv_attr( 'using_named' )
393             unless @_ > 1;
394             if ( $sth->_priv_attr( using_named => !!$value ) ) {
395             $sth->_priv_attr( using_positional => '',
396             using_numbered => '' );
397             }
398             return $sth;
399             }
400              
401             sub _using_numbered
402             {
403             my ( $sth, $value ) = @_;
404             return $sth->_priv_attr( 'using_numbered' ) unless @_ > 1;
405             if ( $sth->_priv_attr( using_numbered => !!$value ) ) {
406             $sth->_priv_attr( using_positional => '',
407             using_named => '' );
408             }
409             return $sth;
410             }
411              
412             sub _using_positional
413             {
414             my ( $sth, $value ) = @_;
415             return $sth->_priv_attr( 'using_positional' ) unless @_ > 1;
416             if ( $sth->_priv_attr( using_positional => !!$value ) ) {
417             $sth->_priv_attr( using_numbered => '',
418             using_named => '' );
419             }
420             return $sth;
421             }
422              
423             sub _init_private_attributes
424             {
425             my ( $sth, $params_arrayref ) = @_;
426             return $sth unless _is_arrayref( $params_arrayref );
427             $sth->_priv_attr;
428             $sth->_param_order( $params_arrayref );
429             return $sth->_using_positional( 1 )
430             unless @$params_arrayref;
431             $sth->_auto_bind( $DBIx::FlexibleBinding::AUTO_BINDING_ENABLED );
432             my $param_count = $sth->_param_count;
433             for my $param ( @$params_arrayref ) {
434              
435             if ( defined $param_count->{$param} ) {
436             $param_count->{$param}++;
437             }
438             else {
439             $param_count->{$param} = 1;
440             }
441             }
442             return $sth->_using_named( 1 )
443             if any {/\D/} @$params_arrayref;
444             return $sth->_using_numbered( 1 );
445             }
446              
447             sub _bind_arrayref
448             {
449             my ( $sth, $arrayref ) = @_;
450             for ( my $n = 0; $n < @$arrayref; $n++ ) {
451             $sth->bind_param( $n + 1, $arrayref->[$n] );
452             }
453             return $sth;
454             }
455              
456             sub _bind_hashref
457             {
458             my ( $sth, $hashref ) = @_;
459             while ( my ( $k, $v ) = each %$hashref ) {
460             $sth->bind_param( $k, $v );
461             }
462             return $sth;
463             }
464              
465             sub _bind
466             {
467             my ( $sth, @args ) = @_;
468             return $sth
469             unless @args;
470             return $sth->_bind_arrayref( \@args )
471             if $sth->_using_positional;
472             if ( @args == 1 && ref( $args[0] ) ) {
473             return $sth->set_err( $DBI::stderr, ERR_EXP_AHR )
474             unless _is_arrayref( $args[0] ) || _is_hashref( $args[0] );
475             return $sth->_bind_hashref( $args[0] )
476             if _is_hashref( $args[0] );
477             return $sth->_bind_arrayref( $args[0] )
478             if $sth->_using_numbered;
479             return $sth->_bind_hashref( { @{ $args[0] } } );
480             }
481             if ( @args ) {
482             return $sth->_bind_arrayref( \@args )
483             if $sth->_using_numbered;
484             return $sth->_bind_hashref( {@args} );
485             }
486             return $sth;
487             }
488              
489             sub bind_param
490             {
491             my ( $sth, $param, $value, $attr ) = @_;
492             return $sth->set_err( $DBI::stderr, ERR_MISSING_BIND_ID )
493             unless $param;
494             return $sth->set_err( $DBI::stderr, ERR_BAD_BIND_ID( $param ) )
495             if $param =~ m{[^\@\w]};
496             my $result;
497             if ( $sth->_using_positional ) {
498             $result = $sth->next::method( $param, $value, $attr );
499             }
500             else {
501             my ( $pos, $count, %param_count ) = ( 0, 0, $sth->_param_count );
502             for my $identifier ( $sth->_param_order ) {
503             $pos++;
504             if ( $identifier eq $param ) {
505             last if ++$count > $param_count{$param};
506             $result = $sth->next::method( $pos, $value, $attr );
507             }
508             }
509             }
510             return $result;
511             }
512              
513             sub execute
514             {
515             my ( $sth, @bind_values ) = @_;
516             my $rows;
517             if ( $sth->_auto_bind ) {
518             $sth->_bind( @bind_values );
519             $rows = $sth->next::method();
520             }
521             else {
522             if ( @bind_values == 1 && _is_arrayref( $bind_values[0] ) ) {
523             $rows = $sth->next::method( @{ $bind_values[0] } );
524             }
525             else {
526             $rows = $sth->next::method( @bind_values );
527             }
528             }
529             return $rows;
530             }
531              
532             sub iterate
533             {
534             my ( $callbacks, $sth, @bind_values ) = &callbacks;
535             my $rows = $sth->execute( @bind_values );
536             return $rows unless defined $rows;
537             my $iter_fn = sub { $sth->getrow( $callbacks ) };
538             return bless( $iter_fn, 'DBIx::FlexibleBinding::Iterator' );
539             }
540              
541             sub getrows_arrayref
542             {
543             my ( $callbacks, $sth, @args ) = &callbacks;
544             unless ( $sth->{Active} ) {
545             $sth->execute( @args );
546             return _as_list_or_ref( undef )
547             if $sth->err;
548             }
549             my $result = $sth->fetchall_arrayref;
550             return _as_list_or_ref( $result )
551             if $sth->err or not defined $result;
552             local $_;
553             $result = [ map { $callbacks->transform( $_ ) } @$result ]
554             if @$callbacks;
555             return _as_list_or_ref( $result );
556             }
557              
558             sub getrows_hashref
559             {
560             my ( $callbacks, $sth, @args ) = &callbacks;
561             unless ( $sth->{Active} ) {
562             $sth->execute( @args );
563             return _as_list_or_ref( undef )
564             if $sth->err;
565             }
566             my $result = $sth->fetchall_arrayref( {} );
567             return _as_list_or_ref( $result )
568             if $sth->err or not defined $result;
569             local $_;
570             $result = [ map { $callbacks->transform( $_ ) } @$result ]
571             if @$callbacks;
572             return _as_list_or_ref( $result );
573             }
574              
575             sub getrow_arrayref
576             {
577             my ( $callbacks, $sth, @args ) = &callbacks;
578             unless ( $sth->{Active} ) {
579             $sth->execute( @args );
580             return undef
581             if $sth->err;
582             }
583             my $result = $sth->fetchrow_arrayref;
584             return $result
585             if $sth->err or not defined $result;
586             local $_;
587             $result = [@$result];
588             $result = $callbacks->smart_transform( $_ = $result )
589             if @$callbacks;
590             return $result;
591             }
592              
593             sub getrow_hashref
594             {
595             my ( $callbacks, $sth, @args ) = &callbacks;
596             unless ( $sth->{Active} ) {
597             $sth->execute( @args );
598             return undef
599             if $sth->err;
600             }
601             my $result = $sth->fetchrow_hashref;
602             return $result
603             if $sth->err or not defined $result;
604             local $_;
605             $result = $callbacks->smart_transform( $_ = $result )
606             if @$callbacks;
607             return $result;
608             }
609              
610             BEGIN {
611             *getrows = \&getrows_hashref;
612             *getrow = \&getrow_hashref;
613             }
614              
615             package # Hide from PAUSE
616             DBIx::FlexibleBinding::Iterator;
617             our $VERSION = '2.0.3'; # VERSION
618              
619             use Params::Callbacks ( 'callbacks' );
620             use namespace::clean;
621              
622             sub for_each
623             {
624             my ( $callbacks, $iter ) = &callbacks;
625             my @results;
626             local $_;
627             while ( my @items = $iter->() ) {
628             last if @items == 1 and not defined $items[0];
629             push @results, map { $callbacks->transform( $_ ) } @items;
630             }
631             return wantarray ? @results : \@results;
632             }
633              
634             1;
635              
636             =pod
637              
638             =encoding utf8
639              
640             =head1 NAME
641              
642             DBIx::FlexibleBinding - Greater flexibility on statement placeholder choice and data binding
643              
644             =head1 VERSION
645              
646             version 2.0.3
647              
648             =head1 SYNOPSIS
649              
650             This module extends the DBI allowing you choose from a variety of supported
651             parameter placeholder and binding patterns as well as offering simplified
652             ways to interact with datasources, while improving general readability.
653              
654             #########################################################
655             # SCENARIO 1 #
656             # A connect followed by a prepare-execute-process cycle #
657             #########################################################
658              
659             use DBIx::FlexibleBinding;
660             use constant DSN => 'dbi:mysql:test;host=127.0.0.1';
661             use constant SQL => << '//';
662             SELECT solarSystemName AS name
663             FROM mapsolarsystems
664             WHERE regional = :is_regional
665             AND security >= :minimum_security
666             //
667              
668             # Pretty standard connect, just with the new DBI subclass ...
669             #
670             my $dbh = DBIx::FlexibleBinding->connect(DSN, '', '', { RaiseError => 1 });
671              
672             # Prepare statement using named placeholders (not bad for MySQL, eh) ...
673             #
674             my $sth = $dbh->prepare(SQL);
675              
676             # Execute the statement (parameter binding is automatic) ...
677             #
678             my $rv = $sth->execute(is_regional => 1,
679             minimum_security => 1.0);
680              
681             # Fetch and transform rows with a blocking callback to get only the data you
682             # want without cluttering the place up with intermediate state ...
683             #
684             my @system_names = $sth->getrows_hashref(callback { $_->{name} });
685              
686             ############################################################################
687             # SCENARIO 2 #
688             # Let's simplify the previous scenario using the database handle's version #
689             # of that getrows_hashref method. #
690             ############################################################################
691              
692             use DBIx::FlexibleBinding -alias => 'DFB';
693             use constant DSN => 'dbi:mysql:test;host=127.0.0.1';
694             use constant SQL => << '//';
695             SELECT solarSystemName AS name
696             FROM mapsolarsystems
697             WHERE regional = :is_regional
698             AND security >= :minimum_security
699             //
700              
701             # Pretty standard connect, this time with the DBI subclass package alias ...
702             #
703             my $dbh = DFB->connect(DSN, '', '', { RaiseError => 1 });
704              
705             # Cut out the middle men ...
706             #
707             my @system_names = $dbh->getrows_hashref(SQL,
708             is_regional => 1,
709             minimum_security => 1.0,
710             callback { $_->{name} });
711              
712             #############################################################################
713             # SCENARIO 3 #
714             # The subclass import method provides a versatile mechanism for simplifying #
715             # matters further. #
716             #############################################################################
717              
718             use DBIx::FlexibleBinding -subs => [ 'MyDB' ];
719             use constant DSN => 'dbi:mysql:test;host=127.0.0.1';
720             use constant SQL => << '//';
721             SELECT solarSystemName AS name
722             FROM mapsolarsystems
723             WHERE regional = :is_regional
724             AND security >= :minimum_security
725             //
726              
727             # MyDB will represent our datasource; initialise it ...
728             #
729             MyDB DSN, '', '', { RaiseError => 1 };
730              
731             # Cut out the middle men and some of the line-noise, too ...
732             #
733             my @system_names = MyDB(SQL,
734             is_regional => 1,
735             minimum_security => 1.0,
736             callback { $_->{name} });
737             =head1 DESCRIPTION
738              
739             This module subclasses the DBI to provide improvements and greater flexibility
740             in the following areas:
741              
742             =over 2
743              
744             =item * Parameter placeholders and data binding
745              
746             =item * Data retrieval and processing
747              
748             =item * Accessing and interacting with datasources
749              
750             =back
751              
752             =head2 Parameter placeholders and data binding
753              
754             This module provides support for a wider range of parameter placeholder and
755             data-binding schemes. As well as continued support for the simple positional
756             placeholders (C), additional support is provided for numeric placeholders (C<:N>
757             and C), and named placeholders (C<:NAME> and C<@NAME>).
758              
759             As for the process of binding data values to parameters: that is, by default,
760             now completely automated, removing a significant part of the workload from the
761             prepare-bind-execute cycle. It is, however, possible to swtch off automatic
762             data-binding globally and on a statement-by-statement basis.
763              
764             The following familiar operations have been modified to accommodate all of these
765             changes, though developers continue to use them as they always have done:
766              
767             =over 2
768              
769             =item * C<$DATABASE_HANDLE-Eprepare($STATEMENT, \%ATTR);>
770              
771             =item * C<$DATABASE_HANDLE-Edo($STATEMENT, \%ATTR, @DATA);>
772              
773             =item * C<$STATEMENT_HANDLE-Ebind_param($NAME_OR_POSITION, $VALUE, \%ATTR);>
774              
775             =item * C<$STATEMENT_HANDLE-Eexecute(@DATA);>
776              
777             =back
778              
779             =head2 Data retrieval and processing
780              
781             Four new methods, each available for database B statement handles, have
782             been implemented:
783              
784             =over 2
785              
786             =item * C
787              
788             =item * C
789              
790             =item * C
791              
792             =item * C
793              
794             =back
795              
796             These methods complement DBI's existing fetch methods, providing new ways to
797             retrieve and process data.
798              
799             =head2 Accessing and interacting with datasources
800              
801             The module's C<-subs> import option may be used to create subroutines,
802             during the compile phase, and export them to the caller's namespace for
803             use later as representations of database and statement handles.
804              
805             =over 2
806              
807             =item * Use for connecting to datasources
808              
809             use DBIx::FlexibleBinding -subs => [ 'MyDB' ];
810              
811             # Pass in any set of well-formed DBI->connect(...) arguments to associate
812             # your name with a live database connection ...
813             #
814             MyDB( 'dbi:mysql:test;host=127.0.0.1', '', '', { RaiseError => 1 } );
815              
816             # Or, simply pass an existing database handle as the only argument ...
817             #
818             MyDB($dbh);
819              
820             =item * Use them to represent database handles
821              
822             use DBIx::FlexibleBinding -subs => [ 'MyDB' ];
823             use constant SQL => << '//';
824             SELECT *
825             FROM mapsolarsystems
826             WHERE regional = :is_regional
827             AND security >= :minimum_security
828             //
829              
830             MyDB( 'dbi:mysql:test;host=127.0.0.1', '', '', { RaiseError => 1 } );
831              
832             # If your name is already associated with a database handle then just call
833             # it with no parameters to use it as such ...
834             #
835             my $sth = MyDB->prepare(SQL);
836              
837             =item * Use them to represent statement handles
838              
839             use DBIx::FlexibleBinding -subs => [ 'MyDB', 'solar_systems' ];
840             use constant SQL => << '//';
841             SELECT *
842             FROM mapsolarsystems
843             WHERE regional = :is_regional
844             AND security >= :minimum_security
845             //
846              
847             MyDB( 'dbi:mysql:test;host=127.0.0.1', '', '', { RaiseError => 1 } );
848              
849             my $sth = MyDB->prepare(SQL);
850              
851             # Simply call the statement handle proxy, passing a statement handle in as
852             # the only argument ...
853             #
854             solar_systems($sth);
855              
856             =item * Use to interact with the represented database and statement handles
857              
858             use DBIx::FlexibleBinding -subs => [ 'MyDB', 'solar_systems' ];
859             use constant SQL => << '//';
860             SELECT *
861             FROM mapsolarsystems
862             WHERE regional = :is_regional
863             AND security >= :minimum_security
864             //
865              
866             MyDB( 'dbi:mysql:test;host=127.0.0.1', '', '', { RaiseError => 1 } );
867              
868             # Use the database handle proxy to prepare, bind and execute statements, then
869             # retrieve the results ...
870             #
871             # Use the database handle proxy to prepare, bind and execute statements, then
872             # retrieve the results ...
873             #
874             my $array_of_hashrefs = MyDB(SQL,
875             is_regional => 1,
876             minimum_security => 1.0);
877              
878             # In list context, results come back as lists ...
879             #
880             my @array_of_hashrefs = MyDB(SQL,
881             is_regional => 1,
882             minimum_security => 1.0);
883              
884             # You can use proxies to represent statements, too. Simply pass in a statement
885             # handle as the only argument ...
886             #
887             my $sth = MyDB->prepare(SQL);
888             solar_systems($sth); # Using "solar_systems" as statement proxy.
889              
890             # Now, when called with other types of arguments, those argument values are
891             # bound and the statement is executed ...
892             #
893             my $array_of_hashrefs = solar_systems(is_regional => 1,
894             minimum_security => 1.0);
895              
896             # In list context, results come back as lists ...
897             #
898             my @array_of_hashrefs = solar_systems(is_regional => 1,
899             minimum_security => 1.0);
900              
901             # Statements requiring no parameters cannot be used in this manner because
902             # making a call to a statement proxy with an arity of zero results in the
903             # statement handle being returned. In this situation, use something like
904             # undef as an argument (it will be ignored in this particular instance) ...
905             #
906             my $rv = statement_proxy(undef);
907             #
908             # Meh, you can't win 'em all!
909              
910             =back
911              
912             =head1 PACKAGE GLOBALS
913              
914             =head2 $DBIx::FlexibleBinding::AUTO_BINDING_ENABLED
915              
916             A boolean setting used to determine whether or not automatic binding is enabled
917             or disabled globally.
918              
919             The default setting is C<"1"> (I).
920              
921             =head1 IMPORT TAGS AND OPTIONS
922              
923             =head2 -alias
924              
925             This option may be used by the caller to select an alias to use for this
926             package's unwieldly namespace.
927              
928             use DBIx::FlexibleBinding -alias => 'DBIF';
929              
930             my $dbh = DBIF->connect('dbi:SQLite:test.db', '', '');
931              
932             =head2 -subs
933              
934             This option may be used to create subroutines, during the compile phase, in
935             the caller's namespace to be used as representations of database and statement
936             handles.
937              
938             use DBIx::FlexibleBinding -subs => [ 'MyDB' ];
939              
940             # Initialise by passing in a valid set of DBI->connect(...) arguments.
941             # The database handle will be the return value.
942             #
943             MyDB 'dbi:mysql:test;host=127.0.0.1', '', '', { RaiseError => 1 };
944              
945             # Or, initialise by passing in a DBI database handle.
946             # The handle is also the return value.
947             #
948             MyDB $dbh;
949              
950             # Once initialised, use the subroutine as you would a DBI database handle.
951             #
952             my $statement = << '//';
953             SELECT solarSystemName AS name
954             FROM mapsolarsystems
955             WHERE security >= :minimum_security
956             //
957             my $sth = MyDB->prepare($statement);
958              
959             # Or use it as an expressive time-saver!
960             #
961             my $array_of_hashrefs = MyDB($statement, security => 1.0);
962             my @system_names = MyDB($statement, minimum_security => 1.0, callback {
963             return $_->{name};
964             });
965             MyDB $statement, minimum_security => 1.0, callback {
966             my ($row) = @_;
967             print "$row->{name}\n";
968             };
969              
970             =head1 CLASS METHODS
971              
972             =head2 connect
973              
974             $dbh = DBIx::FlexibleBinding->connect($data_source, $user, $pass)
975             or die $DBI::errstr;
976             $dbh = DBIx::FlexibleBinding->connect($data_source, $user, $pass, \%attr)
977             or die $DBI::errstr;
978              
979             Establishes a database connection, or session, to the requested data_source and
980             returns a database handle object if the connection succeeds or undef if it does
981             not.
982              
983             Refer to L for a more detailed
984             description of this method.
985              
986             =head1 DATABASE HANDLE METHODS
987              
988             =head2 do
989              
990             $rows = $dbh->do($statement_string) or die $dbh->errstr;
991             $rows = $dbh->do($statement_string, @bind_values) or die $dbh->errstr;
992             $rows = $dbh->do($statement_string, \%attr) or die $dbh->errstr;
993             $rows = $dbh->do($statement_string, \%attr, @bind_values) or die $dbh->errstr;
994             $rows = $dbh->do($statement_handle) or die $dbh->errstr;
995             $rows = $dbh->do($statement_handle, @bind_values) or die $dbh->errstr;
996              
997              
998             Prepares (if necessary) and executes a single statement. Returns the number of
999             rows affected or undef on error. A return value of -1 means the number of rows
1000             is not known, not applicable, or not available. When no rows have been affected
1001             this method continues the C tradition of returning C<0E0> on successful
1002             execution and C on failure.
1003              
1004             The C method accepts optional callbacks for further processing of the result.
1005              
1006             The C implementation provided by this module allows for some minor
1007             deviations in usage over the standard C implementation. In spite
1008             of this, the new method may be used just like the original.
1009              
1010             Refer to L for a more detailed
1011             description of this method.
1012              
1013             B
1014              
1015             =over
1016              
1017             =item 1. Statement attributes are now optional:
1018              
1019             $sql = << '//';
1020             UPDATE employees
1021             SET salary = :salary
1022             WHERE employee_id = :employee_id
1023             //
1024              
1025             $dbh->do($sql, employee_id => 52, salary => 35_000)
1026             or die $dbh->errstr;
1027              
1028             A reference to the statement attributes hash is no longer required, even if it's
1029             empty. If, however, a hash reference is supplied as the first parameter then it
1030             would be used for that purpose.
1031              
1032             =item 2. Prepared statements now may be re-used:
1033              
1034             $sth = $dbh->prepare(<< '//');
1035             UPDATE employees
1036             SET salary = ?
1037             WHERE employee_id = ?
1038             //
1039              
1040             $dbh->do($sth, 35_000, 52) or die $dbh->errstr;
1041              
1042             A prepared statement may also be used in lieu of a statement string. In such
1043             cases, referencing a statement attributes hash is neither required nor expected.
1044              
1045             =back
1046              
1047             =head2 prepare
1048              
1049             $sth = $dbh->prepare($statement_string);
1050             $sth = $dbh->prepare($statement_string, \%attr);
1051              
1052             Prepares a statement for later execution by the database engine and returns a
1053             reference to a statement handle object.
1054              
1055             Refer to L for a more detailed
1056             description of this method.
1057              
1058             B
1059              
1060             =over
1061              
1062             =item 1. Prepare a statement using positional placeholders:
1063              
1064             $sql = << '//';
1065             UPDATE employees
1066             SET salary = ?
1067             WHERE employee_id = ?
1068             //
1069              
1070             $sth = $dbh->prepare($sql);
1071              
1072             =item 2. Prepare a statement using named placeholders:
1073              
1074             I<(Yes, even for those MySQL connections!)>
1075              
1076             $sql = << '//';
1077             UPDATE employees
1078             SET salary = :salary
1079             WHERE employee_id = :employee_id
1080             //
1081              
1082             $sth = $dbh->prepare($sql);
1083              
1084             =back
1085              
1086             =head2 getrows_arrayref I<(database handles)>
1087              
1088             $results = $dbh->getrows_arrayref($statement_string, @bind_values);
1089             @results = $dbh->getrows_arrayref($statement_string, @bind_values);
1090             $results = $dbh->getrows_arrayref($statement_string, \%attr, @bind_values);
1091             @results = $dbh->getrows_arrayref($statement_string, \%attr, @bind_values);
1092             $results = $dbh->getrows_arrayref($statement_handle, @bind_values);
1093             @results = $dbh->getrows_arrayref($statement_handle, @bind_values);
1094              
1095             Prepares (if necessary) and executes a single statement with the specified data
1096             bindings and fetches the result set as an array of array references.
1097              
1098             The C method accepts optional callbacks for further processing
1099             of the results by the caller.
1100              
1101             B
1102              
1103             =over
1104              
1105             =item 1. Prepare, execute it then get the results as a reference:
1106              
1107             $sql = << '//';
1108             SELECT solarSystemName AS name
1109             , security
1110             FROM mapsolarsystems
1111             WHERE regional = 1
1112             AND security >= :minimum_security
1113             //
1114              
1115             $systems = $dbh->getrows_arrayref($sql, minimum_security => 1.0);
1116              
1117             # Returns a structure something like this:
1118             #
1119             # [ [ 'Kisogo', '1' ],
1120             # [ 'New Caldari', '1' ],
1121             # [ 'Amarr', '1' ],
1122             # [ 'Bourynes', '1' ],
1123             # [ 'Ryddinjorn', '1' ],
1124             # [ 'Luminaire', '1' ],
1125             # [ 'Duripant', '1' ],
1126             # [ 'Yulai', '1' ] ]
1127              
1128             =item 2. Re-use a prepared statement, execute it then return the results as a list:
1129              
1130             We'll use the query from Example 1 but have the results returned as a list for
1131             further processing by the caller.
1132              
1133             $sth = $dbh->prepare($sql);
1134              
1135             @systems = $dbh->getrows_arrayref($sql, minimum_security => 1.0);
1136              
1137             for my $system (@systems) {
1138             printf "%-11s %.1f\n", @$system;
1139             }
1140              
1141             # Output:
1142             #
1143             # Kisogo 1.0
1144             # New Caldari 1.0
1145             # Amarr 1.0
1146             # Bourynes 1.0
1147             # Ryddinjorn 1.0
1148             # Luminaire 1.0
1149             # Duripant 1.0
1150             # Yulai 1.0
1151              
1152             =item 3. Re-use a prepared statement, execute it then return modified results as a
1153             reference:
1154              
1155             We'll use the query from Example 1 but have the results returned as a list
1156             for further processing by a caller who will be using callbacks to modify those
1157             results.
1158              
1159             $sth = $dbh->prepare($sql);
1160              
1161             $systems = $dbh->getrows_arrayref($sql, minimum_security => 1.0, callback {
1162             my ($row) = @_;
1163             return sprintf("%-11s %.1f\n", @$row);
1164             });
1165              
1166             # Returns a structure something like this:
1167             #
1168             # [ 'Kisogo 1.0',
1169             # 'New Caldari 1.0',
1170             # 'Amarr 1.0',
1171             # 'Bourynes 1.0',
1172             # 'Ryddinjorn 1.0',
1173             # 'Luminaire 1.0',
1174             # 'Duripant 1.0',
1175             # 'Yulai 1.0' ]
1176              
1177             =back
1178              
1179             =head2 getrows_hashref I<(database handles)>
1180              
1181             $results = $dbh->getrows_hashref($statement_string, @bind_values);
1182             @results = $dbh->getrows_hashref($statement_string, @bind_values);
1183             $results = $dbh->getrows_hashref($statement_string, \%attr, @bind_values);
1184             @results = $dbh->getrows_hashref($statement_string, \%attr, @bind_values);
1185             $results = $dbh->getrows_hashref($statement_handle, @bind_values);
1186             @results = $dbh->getrows_hashref($statement_handle, @bind_values);
1187              
1188             Prepares (if necessary) and executes a single statement with the specified data
1189             bindings and fetches the result set as an array of hash references.
1190              
1191             The C method accepts optional callbacks for further processing
1192             of the results by the caller.
1193              
1194             B
1195              
1196             =over
1197              
1198             =item 1. Prepare, execute it then get the results as a reference:
1199              
1200             $sql = << '//';
1201             SELECT solarSystemName AS name
1202             , security
1203             FROM mapsolarsystems
1204             WHERE regional = 1
1205             AND security >= :minimum_security
1206             //
1207              
1208             $systems = $dbh->getrows_hashref($sql, minimum_security => 1.0);
1209              
1210             # Returns a structure something like this:
1211             #
1212             # [ { name => 'Kisogo', security => '1' },
1213             # { name => 'New Caldari', security => '1' },
1214             # { name => 'Amarr', security => '1' },
1215             # { name => 'Bourynes', security => '1' },
1216             # { name => 'Ryddinjorn', security => '1' },
1217             # { name => 'Luminaire', security => '1' },
1218             # { name => 'Duripant', security => '1' },
1219             # { name => 'Yulai', security => '1' } ]
1220              
1221             =item 2. Re-use a prepared statement, execute it then return the results as a list:
1222              
1223             We'll use the query from Example 1 but have the results returned as a list for
1224             further processing by the caller.
1225              
1226             $sth = $dbh->prepare($sql);
1227              
1228             @systems = $dbh->getrows_hashref($sql, minimum_security => 1.0);
1229              
1230             for my $system (@systems) {
1231             printf "%-11s %.1f\n", @{$system}{'name', 'security'}; # Hash slice
1232             }
1233              
1234             # Output:
1235             #
1236             # Kisogo 1.0
1237             # New Caldari 1.0
1238             # Amarr 1.0
1239             # Bourynes 1.0
1240             # Ryddinjorn 1.0
1241             # Luminaire 1.0
1242             # Duripant 1.0
1243             # Yulai 1.0
1244              
1245             =item 3. Re-use a prepared statement, execute it then return modified results as a
1246             reference:
1247              
1248             We'll use the query from Example 1 but have the results returned as a list
1249             for further processing by a caller who will be using callbacks to modify those
1250             results.
1251              
1252             $sth = $dbh->prepare($sql);
1253              
1254             $systems = $dbh->getrows_hashref($sql, minimum_security => 1.0, callback {
1255             sprintf("%-11s %.1f\n", @{$_}{'name', 'security'}); # Hash slice
1256             });
1257              
1258             # Returns a structure something like this:
1259             #
1260             # [ 'Kisogo 1.0',
1261             # 'New Caldari 1.0',
1262             # 'Amarr 1.0',
1263             # 'Bourynes 1.0',
1264             # 'Ryddinjorn 1.0',
1265             # 'Luminaire 1.0',
1266             # 'Duripant 1.0',
1267             # 'Yulai 1.0' ]
1268              
1269             =back
1270              
1271             =head2 getrows I<(database handles)>
1272              
1273             $results = $dbh->getrows($statement_string, @bind_values);
1274             @results = $dbh->getrows($statement_string, @bind_values);
1275             $results = $dbh->getrows($statement_string, \%attr, @bind_values);
1276             @results = $dbh->getrows($statement_string, \%attr, @bind_values);
1277             $results = $dbh->getrows($statement_handle, @bind_values);
1278             @results = $dbh->getrows$statement_handle, @bind_values);
1279              
1280             Alias for C.
1281              
1282             If array references are preferred, have the symbol table glob point alias the
1283             C method.
1284              
1285             The C method accepts optional callbacks for further processing
1286             of the results by the caller.
1287              
1288             =head2 getrow_arrayref I<(database handles)>
1289              
1290             $result = $dbh->getrow_arrayref($statement_string, @bind_values);
1291             $result = $dbh->getrow_arrayref($statement_string, \%attr, @bind_values);
1292             $result = $dbh->getrow_arrayref($statement_handle, @bind_values);
1293              
1294             Prepares (if necessary) and executes a single statement with the specified data
1295             bindings and fetches the first row as an array reference.
1296              
1297             The C method accepts optional callbacks for further processing
1298             of the result by the caller.
1299              
1300             =head2 getrow_hashref I<(database handles)>
1301              
1302             $result = $dbh->getrow_hashref($statement_string, @bind_values);
1303             $result = $dbh->getrow_hashref($statement_string, \%attr, @bind_values);
1304             $result = $dbh->getrow_hashref($statement_handle, @bind_values);
1305              
1306             Prepares (if necessary) and executes a single statement with the specified data
1307             bindings and fetches the first row as a hash reference.
1308              
1309             The C method accepts optional callbacks for further processing
1310             of the result by the caller.
1311              
1312             =head2 getrow I<(database handles)>
1313              
1314             $result = $dbh->getrow($statement_string, @bind_values);
1315             $result = $dbh->getrow($statement_string, \%attr, @bind_values);
1316             $result = $dbh->getrow($statement_handle, @bind_values);
1317              
1318             Alias for C.
1319              
1320             If array references are preferred, have the symbol table glob point alias the
1321             C method.
1322              
1323             The C method accepts optional callbacks for further processing
1324             of the result by the caller.
1325              
1326             =head1 STATEMENT HANDLE METHODS
1327              
1328             =head2 bind_param
1329              
1330             $sth->bind_param($param_num, $bind_value)
1331             $sth->bind_param($param_num, $bind_value, \%attr)
1332             $sth->bind_param($param_num, $bind_value, $bind_type)
1333              
1334             $sth->bind_param($param_name, $bind_value)
1335             $sth->bind_param($param_name, $bind_value, \%attr)
1336             $sth->bind_param($param_name, $bind_value, $bind_type)
1337              
1338             The C method associates (binds) a value to a placeholder embedded in the
1339             prepared statement. The implementation provided by this module allows the use of
1340             parameter names, if appropriate, in addition to parameter positions.
1341              
1342             I for a more detailed
1343             explanation of how to use this method>.
1344              
1345             =head2 execute
1346              
1347             $rv = $sth->execute() or die $DBI::errstr;
1348             $rv = $sth->execute(@bind_values) or die $DBI::errstr;
1349              
1350             Perform whatever processing is necessary to execute the prepared statement. An
1351             C is returned if an error occurs. A successful call returns true regardless
1352             of the number of rows affected, even if it's zero.
1353              
1354             Refer to L for a more detailed
1355             description of this method.
1356              
1357             B
1358              
1359             =over
1360              
1361             =item Use prepare, execute and getrow_hashref with a callback to modify my data:
1362              
1363             use strict;
1364             use warnings;
1365              
1366             use DBIx::FlexibleBinding -subs => [ 'TestDB' ];
1367             use Data::Dumper;
1368             use Test::More;
1369              
1370             $Data::Dumper::Terse = 1;
1371             $Data::Dumper::Indent = 1;
1372              
1373             TestDB 'dbi:mysql:test', '', '', { RaiseError => 1 };
1374              
1375             my $sth = TestDB->prepare(<< '//');
1376             SELECT solarSystemID AS id
1377             , solarSystemName AS name
1378             , security
1379             FROM mapsolarsystems
1380             WHERE solarSystemName RLIKE "^U[^0-9\-]+$"
1381             ORDER BY id, name, security DESC
1382             LIMIT 5
1383             //
1384              
1385             $sth->execute() or die $DBI::errstr;
1386              
1387             my @rows;
1388             my @callback_list = (
1389             callback {
1390             my ($row) = @_;
1391             $row->{filled_with} = ( $row->{security} >= 0.5 )
1392             ? 'Carebears' : 'Yarrbears';
1393             $row->{security} = sprintf('%.1f', $row->{security});
1394             return $row;
1395             }
1396             );
1397              
1398             while ( my $row = $sth->getrow_hashref(@callback_list) ) {
1399             push @rows, $row;
1400             }
1401              
1402             my $expected_result = [
1403             {
1404             'name' => 'Uplingur',
1405             'filled_with' => 'Yarrbears',
1406             'id' => '30000037',
1407             'security' => '0.4'
1408             },
1409             {
1410             'security' => '0.4',
1411             'id' => '30000040',
1412             'name' => 'Uzistoon',
1413             'filled_with' => 'Yarrbears'
1414             },
1415             {
1416             'name' => 'Usroh',
1417             'filled_with' => 'Carebears',
1418             'id' => '30000068',
1419             'security' => '0.6'
1420             },
1421             {
1422             'filled_with' => 'Yarrbears',
1423             'name' => 'Uhtafal',
1424             'id' => '30000101',
1425             'security' => '0.5'
1426             },
1427             {
1428             'security' => '0.3',
1429             'id' => '30000114',
1430             'name' => 'Ubtes',
1431             'filled_with' => 'Yarrbears'
1432             }
1433             ];
1434              
1435             is_deeply( \@rows, $expected_result, 'iterate' )
1436             and diag( Dumper(\@rows) );
1437             done_testing();
1438              
1439             =back
1440              
1441             =head2 iterate
1442              
1443             $iterator = $sth->iterate() or die $DBI::errstr;
1444             $iterator = $sth->iterate(@bind_values) or die $DBI::errstr;
1445              
1446             Perform whatever processing is necessary to execute the prepared statement. An
1447             C is returned if an error occurs. A successful call returns an iterator
1448             which can be used to traverse the result set.
1449              
1450             B
1451              
1452             =over
1453              
1454             =item 1. Using an iterator and callbacks to process the result set:
1455              
1456             use strict;
1457             use warnings;
1458              
1459             use DBIx::FlexibleBinding -subs => [ 'TestDB' ];
1460             use Data::Dumper;
1461             use Test::More;
1462              
1463             $Data::Dumper::Terse = 1;
1464             $Data::Dumper::Indent = 1;
1465              
1466             my @drivers = grep { /^SQLite$/ } DBI->available_drivers();
1467              
1468             SKIP: {
1469             skip("iterate tests (No DBD::SQLite installed)", 1) unless @drivers;
1470              
1471             TestDB "dbi:SQLite:test.db", '', '', { RaiseError => 1 };
1472              
1473             my $sth = TestDB->prepare(<< '//');
1474             SELECT solarSystemID AS id
1475             , solarSystemName AS name
1476             , security
1477             FROM mapsolarsystems
1478             WHERE solarSystemName REGEXP "^U[^0-9\-]+$"
1479             ORDER BY id, name, security DESC
1480             LIMIT 5
1481             //
1482              
1483             # Iterate over the result set
1484             # ---------------------------
1485             # We also queue up a sneaky callback to modify each row of data as it
1486             # is fetched from the result set.
1487              
1488             my $it = $sth->iterate( callback {
1489             my ($row) = @_;
1490             $row->{filled_with} = ( $row->{security} >= 0.5 )
1491             ? 'Carebears' : 'Yarrbears';
1492             $row->{security} = sprintf('%.1f', $row->{security});
1493             return $row;
1494             } );
1495              
1496             my @rows;
1497             while ( my $row = $it->() ) {
1498             push @rows, $row;
1499             }
1500              
1501             # Done, now check the results ...
1502              
1503             my $expected_result = [
1504             {
1505             'name' => 'Uplingur',
1506             'filled_with' => 'Yarrbears',
1507             'id' => '30000037',
1508             'security' => '0.4'
1509             },
1510             {
1511             'security' => '0.4',
1512             'id' => '30000040',
1513             'name' => 'Uzistoon',
1514             'filled_with' => 'Yarrbears'
1515             },
1516             {
1517             'name' => 'Usroh',
1518             'filled_with' => 'Carebears',
1519             'id' => '30000068',
1520             'security' => '0.6'
1521             },
1522             {
1523             'filled_with' => 'Yarrbears',
1524             'name' => 'Uhtafal',
1525             'id' => '30000101',
1526             'security' => '0.5'
1527             },
1528             {
1529             'security' => '0.3',
1530             'id' => '30000114',
1531             'name' => 'Ubtes',
1532             'filled_with' => 'Yarrbears'
1533             }
1534             ];
1535              
1536             is_deeply( \@rows, $expected_result, 'iterate' )
1537             and diag( Dumper(\@rows) );
1538             }
1539              
1540             done_testing();
1541              
1542             In this example, we're traversing the result set using an iterator. As we iterate
1543             through the result set, a callback is applied to each row and we're left with
1544             an array of transformed rows.
1545              
1546             =item 2. Using an iterator's C method and callbacks to process the
1547             result set:
1548              
1549             use strict;
1550             use warnings;
1551              
1552             use DBIx::FlexibleBinding -subs => [ 'TestDB' ];
1553             use Data::Dumper;
1554             use Test::More;
1555              
1556             $Data::Dumper::Terse = 1;
1557             $Data::Dumper::Indent = 1;
1558              
1559             my @drivers = grep { /^SQLite$/ } DBI->available_drivers();
1560              
1561             SKIP: {
1562             skip("iterate tests (No DBD::SQLite installed)", 1) unless @drivers;
1563              
1564             TestDB "dbi:SQLite:test.db", '', '', { RaiseError => 1 };
1565              
1566             my $sth = TestDB->prepare(<< '//');
1567             SELECT solarSystemID AS id
1568             , solarSystemName AS name
1569             , security
1570             FROM mapsolarsystems
1571             WHERE solarSystemName REGEXP "^U[^0-9\-]+$"
1572             ORDER BY id, name, security DESC
1573             LIMIT 5
1574             //
1575              
1576             # Iterate over the result set
1577             # ---------------------------
1578             # This time around we call the iterator's "for_each" method to process
1579             # the data. Bonus: we haven't had to store the iterator anywhere or
1580             # pre-declare an empty array to accommodate our rows.
1581              
1582             my @rows = $sth->iterate->for_each( callback {
1583             my ($row) = @_;
1584             $row->{filled_with} = ( $row->{security} >= 0.5 )
1585             ? 'Carebears' : 'Yarrbears';
1586             $row->{security} = sprintf('%.1f', $row->{security});
1587             return $row;
1588             } );
1589              
1590             # Done, now check the results ...
1591              
1592             my $expected_result = [
1593             {
1594             'name' => 'Uplingur',
1595             'filled_with' => 'Yarrbears',
1596             'id' => '30000037',
1597             'security' => '0.4'
1598             },
1599             {
1600             'security' => '0.4',
1601             'id' => '30000040',
1602             'name' => 'Uzistoon',
1603             'filled_with' => 'Yarrbears'
1604             },
1605             {
1606             'name' => 'Usroh',
1607             'filled_with' => 'Carebears',
1608             'id' => '30000068',
1609             'security' => '0.6'
1610             },
1611             {
1612             'filled_with' => 'Yarrbears',
1613             'name' => 'Uhtafal',
1614             'id' => '30000101',
1615             'security' => '0.5'
1616             },
1617             {
1618             'security' => '0.3',
1619             'id' => '30000114',
1620             'name' => 'Ubtes',
1621             'filled_with' => 'Yarrbears'
1622             }
1623             ];
1624              
1625             is_deeply( \@rows, $expected_result, 'iterate' )
1626             and diag( Dumper(\@rows) );
1627             }
1628              
1629             done_testing();
1630              
1631             Like the previous example, we're traversing the result set using an iterator but
1632             this time around we have done away with C<$it> in favour of calling the iterator's
1633             own C method. The callback we were using to process each row of the
1634             result set has now been passed into the C method also eliminating a
1635             C loop and an empty declaration for C<@rows>.
1636              
1637             =back
1638              
1639             =head2 getrows_arrayref I<(database handles)>
1640              
1641             $results = $sth->getrows_arrayref();
1642             @results = $sth->getrows_arrayref();
1643              
1644             Fetches the entire result set as an array of array references.
1645              
1646             The C method accepts optional callbacks for further processing
1647             of the results by the caller.
1648              
1649             =head2 getrows_hashref I<(database handles)>
1650              
1651             $results = $sth->getrows_hashref();
1652             @results = $sth->getrows_hashref();
1653              
1654             Fetches the entire result set as an array of hash references.
1655              
1656             The C method accepts optional callbacks for further processing
1657             of the results by the caller.
1658              
1659             =head2 getrows I<(database handles)>
1660              
1661             $results = $sth->getrows();
1662             @results = $sth->getrows();
1663              
1664             Alias for C.
1665              
1666             If array references are preferred, have the symbol table glob point alias the
1667             C method.
1668              
1669             The C method accepts optional callbacks for further processing
1670             of the results by the caller.
1671              
1672             =head2 getrow_arrayref I<(database handles)>
1673              
1674             $result = $sth->getrow_arrayref();
1675              
1676             Fetches the next row as an array reference. Returns C if there are no more
1677             rows available.
1678              
1679             The C method accepts optional callbacks for further processing
1680             of the result by the caller.
1681              
1682             =head2 getrow_hashref I<(database handles)>
1683              
1684             $result = $sth->getrow_hashref();
1685              
1686             Fetches the next row as a hash reference. Returns C if there are no more
1687             rows available.
1688              
1689             The C method accepts optional callbacks for further processing
1690             of the result by the caller.
1691              
1692             =head2 getrow I<(database handles)>
1693              
1694             $result = $sth->getrow();
1695              
1696             Alias for C.
1697              
1698             If array references are preferred, have the symbol table glob point alias the
1699             C method.
1700              
1701             The C method accepts optional callbacks for further processing
1702             of the result by the caller.
1703              
1704             =head1 EXPORTS
1705              
1706             The following symbols are exported by default:
1707              
1708             =head2 callback
1709              
1710             To enable the namespace using this module to take advantage of the callbacks,
1711             which are one of its main features, without the unnecessary burden of also
1712             including the module that provides the feature I<(see L for
1713             more detailed information)>.
1714              
1715             =head1 SEE ALSO
1716              
1717             =over 2
1718              
1719             =item * L
1720              
1721             =item * L
1722              
1723             =back
1724              
1725             =head1 REPOSITORY
1726              
1727             =over 2
1728              
1729             =item * L
1730              
1731             =item * L
1732              
1733             =back
1734              
1735             =head1 BUGS
1736              
1737             Please report any bugs or feature requests to C, or through
1738             the web interface at L. I will be notified, and then you'll
1739             automatically be notified of progress on your bug as I make changes.
1740              
1741             =head1 SUPPORT
1742              
1743             You can find documentation for this module with the perldoc command.
1744              
1745             perldoc DBIx::FlexibleBinding
1746              
1747              
1748             You can also look for information at:
1749              
1750             =over 4
1751              
1752             =item * RT: CPAN's request tracker (report bugs here)
1753              
1754             L
1755              
1756             =item * AnnoCPAN: Annotated CPAN documentation
1757              
1758             L
1759              
1760             =item * CPAN Ratings
1761              
1762             L
1763              
1764             =item * Search CPAN
1765              
1766             L
1767              
1768             =back
1769              
1770             =head1 ACKNOWLEDGEMENTS
1771              
1772             Many, many thanks to the CPANTesters network.
1773              
1774             Test data set extracted from Fuzzwork's MySQL conversion of CCP's EVE Online Static
1775             Data Export:
1776              
1777             =over 2
1778              
1779             =item * Fuzzwork L
1780              
1781             =item * EVE Online L
1782              
1783             =back
1784              
1785             Eternal gratitude to GitHub contributors:
1786              
1787             =over 2
1788              
1789             =item * Syohei Yoshida L
1790              
1791             =back
1792              
1793             =head1 AUTHOR
1794              
1795             Iain Campbell
1796              
1797             =head1 COPYRIGHT AND LICENSE
1798              
1799             This software is copyright (c) 2012-2015 by Iain Campbell.
1800              
1801             This is free software; you can redistribute it and/or modify it under
1802             the same terms as the Perl 5 programming language system itself.
1803              
1804             =cut