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