File Coverage

blib/lib/DBIx/Squirrel/it.pm
Criterion Covered Total %
statement 251 354 70.9
branch 75 184 40.7
condition 20 29 68.9
subroutine 53 74 71.6
pod 1 34 2.9
total 400 675 59.2


line stmt bran cond sub pod time code
1 9     9   68 use strict;
  9         53  
  9         418  
2 9     9   50 use warnings;
  9         17  
  9         534  
3 9     9   161 use 5.010_001;
  9         31  
4              
5             package # hide from PAUSE
6             DBIx::Squirrel::it;
7              
8             =head1 NAME
9              
10             DBIx::Squirrel::it - Statement iterator iterator base class
11              
12             =head1 SYNOPSIS
13              
14              
15             =head1 DESCRIPTION
16              
17             This module provides a base class for statement iterators. It is usable as
18             is, but is also subclassed by the L (Results) class.
19              
20             =cut
21              
22 9     9   52 use Exporter ();
  9         17  
  9         315  
23 9         742 use Scalar::Util qw(
24             looks_like_number
25             weaken
26 9     9   54 );
  9         28  
27 9     9   60 use Sub::Name 'subname';
  9         20  
  9         615  
28 9         612 use DBIx::Squirrel::util qw(
29             cluckf
30             confessf
31             callbacks_args
32 9     9   58 );
  9         18  
33 9     9   55 use namespace::clean;
  9         19  
  9         74  
34              
35 9     9   3145 use constant E_BAD_STH => 'Expected a statement handle object';
  9         34  
  9         674  
36 9     9   52 use constant E_BAD_SLICE => 'Slice must be a reference to an ARRAY or HASH';
  9         16  
  9         564  
37 9         561 use constant E_BAD_CACHE_SIZE =>
38 9     9   59 'Maximum row count must be an integer greater than zero';
  9         31  
39 9     9   53 use constant W_MORE_ROWS => 'Query would yield more than one result';
  9         55  
  9         571  
40 9     9   68 use constant E_EXP_ARRAY_REF => 'Expected an ARRAY-REF';
  9         19  
  9         1812  
41              
42             BEGIN {
43             require DBIx::Squirrel
44 9 50   9   53 unless keys %DBIx::Squirrel::;
45 9         30 *DBIx::Squirrel::it::VERSION = *DBIx::Squirrel::VERSION;
46 9         195 @DBIx::Squirrel::it::ISA = 'Exporter';
47 9         96 %DBIx::Squirrel::it::EXPORT_TAGS = ( all => [
48             @DBIx::Squirrel::it::EXPORT_OK = qw(
49             database
50             iterator
51             result
52             result_current
53             result_first
54             result_number
55             result_offset
56             result_original
57             result_prev
58             result_previous
59             result_transform
60             statement
61             )
62             ] );
63 9         22 $DBIx::Squirrel::it::DEFAULT_SLICE = []; # Faster!
64 9         29 $DBIx::Squirrel::it::DEFAULT_CACHE_SIZE = 2; # Initial buffer size and autoscaling increment
65 9         22994 $DBIx::Squirrel::it::CACHE_SIZE_LIMIT = 64; # Absolute maximum buffersize
66             }
67              
68             our $_DATABASE;
69             our $_ITERATOR;
70             our $_RESULT;
71             our $_RESULT_FIRST;
72             our $_RESULT_NUMBER;
73             our $_RESULT_OFFSET;
74             our $_RESULT_ORIGINAL;
75             our $_RESULT_PREV;
76             our $_STATEMENT;
77              
78             sub _cache_charge {
79 60     60   119 my( $attr, $self ) = shift->_private_state;
80 60         112 my $sth = $attr->{sth};
81 60 50       321 unless ( $sth->{Executed} ) {
82 0 0       0 return unless defined $self->start;
83             }
84 60 50       322 return unless $sth->{Active};
85 60         130 my( $slice, $cache_size ) = @{$attr}{qw(slice cache_size)};
  60         148  
86 60         2137 my $rows = $sth->fetchall_arrayref( $slice, $cache_size );
87 60 50       155 return 0 unless $rows;
88 60 100       167 unless ( $attr->{cache_size_fixed} ) {
89 25 100       54 if ( $attr->{cache_size} < CACHE_SIZE_LIMIT() ) {
90 12 100       14 $self->_cache_size_auto_adjust if @{$rows} >= $attr->{cache_size};
  12         43  
91             }
92             }
93             $attr->{buffer} = [
94 60 50       140 defined $attr->{buffer} ? ( @{ $attr->{buffer} }, @{$rows} ) : @{$rows},
  60         112  
  60         291  
  0         0  
95             ];
96 60         107 return scalar @{ $attr->{buffer} };
  60         336  
97             }
98              
99             sub _cache_empty {
100 1170     1170   2106 my( $attr, $self ) = shift->_private_state;
101 1170   66     2753 return $attr->{buffer} && @{ $attr->{buffer} } < 1;
102             }
103              
104             # Where rows are buffered until fetched.
105             sub _cache_init {
106 9     9   21 my( $attr, $self ) = shift->_private_state;
107 9 50       100 $attr->{buffer} = [] if $attr->{sth}->{NUM_OF_FIELDS};
108 9         35 return $self;
109             }
110              
111             sub _cache_size_auto_adjust {
112 10     10   16 my( $attr, $self ) = shift->_private_state;
113 10         19 $attr->{cache_size} *= 2;
114             $attr->{cache_size} = CACHE_SIZE_LIMIT()
115 10 50       30 if $attr->{cache_size} > CACHE_SIZE_LIMIT();
116 10         14 return $self;
117             }
118              
119             # How many rows to buffer at a time.
120             sub _cache_size_init {
121 9     9   18 my( $attr, $self ) = shift->_private_state;
122 9 50       39 if ( $attr->{sth}->{NUM_OF_FIELDS} ) {
123 9   66     34 $attr->{cache_size} ||= DEFAULT_CACHE_SIZE();
124 9   100     32 $attr->{cache_size_fixed} ||= !!0;
125             }
126 9         39 return $self;
127             }
128              
129             my %attr_by_id;
130              
131             sub _private_state {
132 6896     6896   10070 my $self = shift;
133 6896         10211 my $id = 0+ $self;
134 6896         9414 my $attr = do {
135 6896 100       14072 $attr_by_id{$id} = {} unless defined $attr_by_id{$id};
136 6896         10697 $attr_by_id{$id};
137             };
138 6896 100       12448 unless (@_) {
139 6892 100       21156 return wantarray ? ( $attr, $self ) : $attr;
140             }
141 4 50       10 unless ( defined( $_[0] ) ) {
142 0         0 delete $attr_by_id{$id};
143 0         0 shift;
144             }
145 4 50       11 if (@_) {
146 4 50       10 $attr_by_id{$id} = {} unless defined $attr_by_id{$id};
147 4 50       11 if ( UNIVERSAL::isa( $_[0], 'HASH' ) ) {
148 4         6 $attr_by_id{$id} = { %{$attr}, %{ $_[0] } };
  4         10  
  4         30  
149             }
150             else {
151 0         0 $attr_by_id{$id} = { %{$attr}, @_ };
  0         0  
152             }
153             }
154 4         11 return $self;
155             }
156              
157             sub _private_state_clear {
158 9     9   19 my( $attr, $self ) = shift->_private_state;
159 9         16 delete $attr->{$_} for do {
160 9         15 local($_);
161 9         22 grep { exists( $attr->{$_} ) } qw(
  54         183  
162             buffer
163             execute_returned
164             results_pending
165             results_count
166             results_first
167             results_last
168             );
169             };
170 9         28 return $self;
171             }
172              
173             sub _private_state_init {
174 9     9   26 shift->_cache_init->_cache_size_init->_results_count_init;
175             }
176              
177             sub _private_state_reset {
178 9     9   28 shift->_private_state_clear->_private_state_init;
179             }
180              
181             sub database {
182 0     0 0 0 return $_DATABASE;
183             }
184              
185             sub iterator {
186 0     0 0 0 return $_ITERATOR;
187             }
188              
189             sub _result_fetch {
190 30     30   70 my( $attr, $self ) = shift->_private_state;
191 30         50 my $sth = $attr->{sth};
192 30         47 my( $transformed, $results, $result );
193             do {
194 1114 100       2233 return $self->_result_fetch_pending if $self->_results_pending;
195 1106 100       2283 return unless $self->is_active;
196 1102 100       2866 if ( $self->_cache_empty ) {
197 60 50       150 return unless $self->_cache_charge;
198             }
199 1102         1536 $result = shift( @{ $attr->{buffer} } );
  1102         2408  
200 1102         5255 ( $results, $transformed ) = $self->_result_process($result);
201 30   100     39 } while $transformed && !@{$results};
  1100         2867  
202 18         26 $result = shift( @{$results} );
  18         34  
203 18 100       85 $self->_results_push_pending($results) if @{$results};
  18         50  
204 18 100       67 $attr->{results_first} = $result unless $attr->{results_count}++;
205 18         35 $attr->{results_last} = $result;
206 18         25 return do { $_ = $result };
  18         73  
207             }
208              
209             sub _result_fetch_pending {
210 8     8   15 my( $attr, $self ) = shift->_private_state;
211 8 50       24 return unless defined( $attr->{results_pending} );
212 8         13 my $result = shift( @{ $attr->{results_pending} } );
  8         17  
213 8 50       23 $attr->{results_first} = $result unless $attr->{results_count}++;
214 8         13 $attr->{results_last} = $result;
215 8         14 return do { $_ = $result };
  8         29  
216             }
217              
218             # Seemingly pointless here, but intended to be overridden in subclasses.
219             sub _result_preprocess {
220 551     551   573 return $_[1];
221             }
222              
223             sub _result_process {
224 1102     1102   2032 my( $attr, $self ) = shift->_private_state;
225 1102         2620 my $result = $self->_result_preprocess(shift);
226 1102         1592 my $transform = !!@{ $attr->{transforms} };
  1102         2196  
227 1102         1785 my @results = do {
228 1102         1534 local($_);
229 1102 100       1877 if ($transform) {
230 1100         2116 local($_DATABASE) = $self->sth->{Database};
231 1100         2526 local($_ITERATOR) = $self;
232 1100         1983 local($_RESULT_FIRST) = $attr->{results_first};
233 1100         1952 local($_RESULT_NUMBER) = $attr->{results_count} + 1;
234 1100         1639 local($_RESULT_OFFSET) = $attr->{results_count};
235 1100         1774 local($_RESULT_PREV) = $attr->{results_last};
236 1100         2061 local($_STATEMENT) = $self->sth;
237             map {
238 1100         1994 result_transform(
239             $attr->{transforms},
240 1100         2486 $self->_result_preprocess($_),
241             )
242             } $result;
243             }
244             else {
245 2         6 $result;
246             }
247             };
248 1102 50       4450 return wantarray ? ( \@results, $transform ) : \@results;
249             }
250              
251             # The total number of rows fetched since execute was called.
252             sub _results_count_init {
253 9     9   19 my( $attr, $self ) = shift->_private_state;
254 9 50       47 $attr->{results_count} = 0 if $attr->{sth}->{NUM_OF_FIELDS};
255 9         20 return $self;
256             }
257              
258             sub _results_pending {
259 1114     1114   2246 my( $attr, $self ) = shift->_private_state;
260 1114 100       2974 return unless defined $attr->{results_pending};
261 600         838 return !!@{ $attr->{results_pending} };
  600         1608  
262             }
263              
264             sub _results_push_pending {
265 4     4   13 my( $attr, $self ) = shift->_private_state;
266 4 50       13 return unless @_;
267 4 50       20 return unless UNIVERSAL::isa( $_[0], 'ARRAY' );
268 4         8 my $results = shift;
269 4 50       20 $attr->{results_pending} = [] unless defined $attr->{results_pending};
270 4         9 push @{ $attr->{results_pending} }, @{$results};
  4         9  
  4         12  
271 4         10 return $self;
272             }
273              
274             sub DEFAULT_CACHE_SIZE {
275 5     5 0 9 my $class = shift;
276 5 50       14 if (@_) {
277 0         0 $DBIx::Squirrel::it::DEFAULT_CACHE_SIZE = shift;
278             }
279 5 50       19 if ( $DBIx::Squirrel::it::DEFAULT_CACHE_SIZE < 2 ) {
280 0         0 $DBIx::Squirrel::it::DEFAULT_CACHE_SIZE = 2;
281             }
282 5         16 return $DBIx::Squirrel::it::DEFAULT_CACHE_SIZE;
283             }
284              
285             sub CACHE_SIZE_LIMIT {
286 37     37 0 1152 my $class = shift;
287 37 50       78 if (@_) {
288 0         0 $DBIx::Squirrel::it::CACHE_SIZE_LIMIT = shift;
289             }
290 37 50       77 if ( $DBIx::Squirrel::it::CACHE_SIZE_LIMIT > 64 ) {
291 0         0 $DBIx::Squirrel::it::CACHE_SIZE_LIMIT = 64;
292             }
293 37         88 return $DBIx::Squirrel::it::CACHE_SIZE_LIMIT;
294             }
295              
296             sub DEFAULT_SLICE {
297 0     0 0 0 my $class = shift;
298 0 0       0 if (@_) {
299 0         0 $DBIx::Squirrel::it::DEFAULT_SLICE = shift;
300             }
301 0         0 return $DBIx::Squirrel::it::DEFAULT_SLICE;
302             }
303              
304             sub DESTROY {
305 0 0   0   0 return if DBIx::Squirrel::util::global_destruct_phase();
306 0         0 my $self = shift;
307 0         0 local( $., $@, $!, $^E, $?, $_ );
308 0         0 $self->_private_state_clear;
309 0         0 $self->_private_state(undef);
310 0         0 return;
311             }
312              
313             sub all {
314 4     4 0 17 return shift->reset->remaining;
315             }
316              
317             sub cache_size {
318 1     1 0 5 my( $attr, $self ) = shift->_private_state;
319 1 50       4 if (@_) {
320 1 50       9 confessf E_BAD_CACHE_SIZE unless looks_like_number( $_[0] );
321 1 50 33     5 confessf E_BAD_CACHE_SIZE
322             if $_[0] < DEFAULT_CACHE_SIZE()
323             || $_[0] > CACHE_SIZE_LIMIT();
324 1         4 $attr->{cache_size} = shift;
325 1         4 $attr->{cache_size_fixed} = !!1;
326 1         9 return $self;
327             }
328             else {
329             $attr->{cache_size} = DEFAULT_CACHE_SIZE()
330 0 0       0 unless defined $attr->{cache_size};
331 0         0 return $attr->{cache_size};
332             }
333             }
334              
335             BEGIN {
336 9     9   1657 *buffer_size = subname( buffer_size => \&cache_size );
337              
338             }
339              
340             sub cache_size_slice {
341 0     0 0 0 my $self = shift;
342 0 0       0 return $self->cache_size, $self->slice unless @_;
343 0 0       0 if ( ref $_[0] ) {
344 0         0 $self->slice(shift);
345 0 0       0 $self->cache_size(shift) if @_;
346             }
347             else {
348 0         0 $self->cache_size(shift);
349 0 0       0 $self->slice(shift) if @_;
350             }
351 0         0 return $self;
352             }
353              
354             BEGIN {
355 9     9   3746 *buffer_size_slice = subname( buffer_size_slice => \&cache_size_slice );
356             }
357              
358             sub count {
359 0     0 0 0 my( $attr, $self ) = shift->_private_state;
360 0 0       0 unless ( $attr->{sth}->{Executed} ) {
361 0 0       0 return unless defined $self->start;
362             }
363 0         0 while ( defined $self->_result_fetch ) { ; }
364 0         0 return $_ = $attr->{results_count};
365             }
366              
367             sub count_fetched {
368 0     0 0 0 my( $attr, $self ) = shift->_private_state;
369 0 0       0 unless ( $attr->{sth}->{Executed} ) {
370 0 0       0 return unless defined $self->start;
371             }
372 0         0 return $_ = $attr->{results_count};
373             }
374              
375             sub first {
376 0     0 0 0 my( $attr, $self ) = shift->_private_state;
377 0 0       0 unless ( $attr->{sth}->{Executed} ) {
378 0 0       0 return unless defined $self->start;
379             }
380 0 0       0 if ( exists $attr->{results_first} ) {
381 0         0 return $_ = $attr->{results_first};
382             }
383             else {
384 0         0 return $_ = $self->_result_fetch;
385             }
386             }
387              
388             sub is_active {
389 1138     1138 0 1940 my( $attr, $self ) = shift->_private_state;
390 1138   100     8345 return $attr->{sth}->{Active} || !$self->_cache_empty;
391             }
392              
393             BEGIN {
394 9     9   99 *active = subname( active => \&is_active );
395 9         5489 *not_done = subname( not_done => \&is_active );
396             }
397              
398             sub iterate {
399 4     4 0 9 my $self = shift;
400 4 50       17 return unless defined $self->start(@_);
401 4         17 return $_ = $self;
402             }
403              
404             sub last {
405 0     0 0 0 my( $attr, $self ) = shift->_private_state;
406 0 0       0 unless ( $attr->{sth}->{Executed} ) {
407 0 0       0 return unless defined $self->start;
408 0         0 while ( defined $self->_result_fetch ) { ; }
409             }
410 0         0 return $_ = $attr->{results_last};
411             }
412              
413             sub last_fetched {
414 0     0 0 0 my( $attr, $self ) = shift->_private_state;
415 0 0       0 unless ( $attr->{sth}->{Executed} ) {
416 0         0 $self->start;
417 0         0 return $_ = undef;
418             }
419 0         0 return $_ = $attr->{results_last};
420             }
421              
422              
423             =head3 C
424              
425             my $it = DBIx::Squirrel::it->new($sth);
426             my $it = DBIx::Squirrel::it->new($sth, @bind_values);
427             my $it = DBIx::Squirrel::it->new($sth, @bind_values, @transforms);
428              
429             Creates a new iterator object.
430              
431             This method is not intended to be called directly, but rather indirectly
432             via the C or C methods of L and
433             L packages.
434              
435             =cut
436              
437             sub new {
438 4 50   4 1 11 my $class = ref $_[0] ? ref shift : shift;
439 4         22 my( $transforms, $sth, @bind_values ) = callbacks_args(@_);
440 4 50       18 confessf E_BAD_STH unless UNIVERSAL::isa( $sth, 'DBIx::Squirrel::st' );
441 4         8 my $self = bless {}, $class;
442 4         31 $self->_private_state( {
443             sth => $sth,
444             bind_values_initial => [@bind_values],
445             transforms_initial => $transforms,
446             } );
447 4         22 return $self;
448             }
449              
450              
451             sub next {
452 2     2 0 5 my $self = shift;
453 2         8 my $sth = $self->sth;
454 2 50       14 unless ( $sth->{Executed} ) {
455 0 0       0 return unless defined $self->start;
456             }
457 2         9 return $_ = $self->_result_fetch;
458             }
459              
460             sub not_active {
461 0     0 0 0 my( $attr, $self ) = shift->_private_state;
462 0   0     0 return !$attr->{sth}->{Active} && $self->_cache_empty;
463             }
464              
465             BEGIN {
466 9     9   89 *inactive = subname( inactive => \¬_active );
467 9         2882 *is_done = subname( is_done => \¬_active );
468             }
469              
470             sub remaining {
471 4     4 0 29 my $self = shift;
472 4         13 my $sth = $self->sth;
473 4 50       42 unless ( $sth->{Executed} ) {
474 0 0       0 return unless defined $self->start;
475             }
476 4         12 my @rows;
477 4         12 while ( $self->not_done ) {
478 28         82 push @rows, $self->_result_fetch;
479             }
480 4 50       31 return wantarray ? @rows : \@rows;
481             }
482              
483             sub reset {
484 4     4 0 7 my $self = shift;
485 4 50       29 if (@_) {
486 0 0       0 if ( ref( $_[0] ) ) {
487 0         0 $self->slice(shift);
488 0 0       0 $self->cache_size(shift) if @_;
489             }
490             else {
491 0         0 $self->cache_size(shift);
492 0 0       0 $self->slice(shift) if @_;
493             }
494             }
495 4         13 $self->start;
496 4         15 return $self;
497             }
498              
499              
500             sub result {
501 3     3 0 13 return $_RESULT;
502             }
503              
504             BEGIN {
505 9     9   1502 *result_current = subname( result_current => \&result );
506             }
507              
508             sub result_first {
509 0     0 0 0 return $_RESULT_FIRST;
510             }
511              
512             sub result_number {
513 0     0 0 0 return $_RESULT_NUMBER;
514             }
515              
516             sub result_offset {
517 0     0 0 0 return $_RESULT_OFFSET;
518             }
519              
520             sub result_original {
521 0     0 0 0 return $_RESULT_ORIGINAL;
522             }
523              
524             sub result_prev {
525 0     0 0 0 return $_RESULT_PREV;
526             }
527              
528             BEGIN {
529 9     9   4244 *result_previous = subname( result_previous => \&result_prev );
530             }
531              
532             sub result_transform { ## not a method
533 1110     1110 0 305866 my @transforms = do {
534 1110 100       2862 if ( UNIVERSAL::isa( $_[0], 'ARRAY' ) ) {
    50          
535 1106         1477 @{ +shift };
  1106         2328  
536             }
537             elsif ( UNIVERSAL::isa( $_[0], 'CODE' ) ) {
538 0         0 shift;
539             }
540             else {
541 4         6 ();
542             }
543             };
544 1110         2234 my @results = @_;
545 1110 100 66     3753 if ( @transforms && @_ ) {
546 1106         1829 local($_RESULT_ORIGINAL) = @results;
547 1106         1931 for my $transform (@transforms) {
548 1125 100       1513 last unless @results = do {
549 1125         1920 local($_) = local($_RESULT) = @results;
550 1125         2485 $transform->(@results);
551             };
552             }
553             }
554 1110 100       7615 return @results if wantarray;
555 2         3 $_ = $results[0];
556 2         5 return @results;
557             }
558              
559             sub rows {
560 0     0 0 0 return shift->sth->rows;
561             }
562              
563             sub single {
564 0     0 0 0 my( $attr, $self ) = shift->_private_state;
565 0 0       0 return unless defined $self->start;
566 0 0       0 return unless defined $self->_result_fetch;
567 0 0       0 cluckf W_MORE_ROWS if @{ $attr->{buffer} };
  0         0  
568 0 0       0 return $_ = exists $attr->{results_first} ? $attr->{results_first} : ();
569             }
570              
571             BEGIN {
572 9     9   3496 *one = subname( one => \&single );
573             }
574              
575             sub slice {
576 0     0 0 0 my( $attr, $self ) = shift->_private_state;
577 0 0       0 if (@_) {
578 0 0       0 if ( ref $_[0] ) {
579 0 0       0 if ( UNIVERSAL::isa( $_[0], 'ARRAY' ) ) {
580 0         0 $attr->{slice} = shift;
581 0         0 return $self;
582             }
583 0 0       0 if ( UNIVERSAL::isa( $_[0], 'HASH' ) ) {
584 0         0 $attr->{slice} = shift;
585 0         0 return $self;
586             }
587             }
588 0         0 confessf E_BAD_SLICE;
589             }
590             else {
591 0 0       0 $attr->{slice} = DEFAULT_SLICE unless $attr->{slice};
592 0         0 return $attr->{slice};
593             }
594             }
595              
596             sub slice_cache_size {
597 0     0 0 0 my $self = shift;
598 0 0       0 return $self->slice, $self->cache_size unless @_;
599 0 0       0 if ( ref $_[0] ) {
600 0         0 $self->slice(shift);
601 0 0       0 $self->cache_size(shift) if @_;
602             }
603             else {
604 0         0 $self->cache_size(shift);
605 0 0       0 $self->slice(shift) if @_;
606             }
607 0         0 return $self;
608             }
609              
610             BEGIN {
611 9     9   3401 *slice_buffer_size = subname( slice_buffer_size => \&slice_cache_size );
612             }
613              
614             sub start {
615 9     9 0 25 my( $attr, $self ) = shift->_private_state;
616 9         37 my( $transforms, @bind_values ) = callbacks_args(@_);
617 9 50       19 if ( @{$transforms} ) {
  9         20  
618 0         0 $attr->{transforms} = [ @{ $attr->{transforms_initial} }, @{$transforms} ];
  0         0  
  0         0  
619             }
620             else {
621 9 100 100     30 unless ( defined $attr->{transforms} && @{ $attr->{transforms} } ) {
  5         23  
622 6         7 $attr->{transforms} = [ @{ $attr->{transforms_initial} } ];
  6         14  
623             }
624             }
625 9 100       19 if (@bind_values) {
626 4         17 $attr->{bind_values} = [@bind_values];
627             }
628             else {
629 5 50 66     20 unless ( defined $attr->{bind_values} && @{ $attr->{bind_values} } ) {
  3         13  
630 5         11 $attr->{bind_values} = [ @{ $attr->{bind_values_initial} } ];
  5         15  
631             }
632             }
633 9         16 my $sth = $attr->{sth};
634 9         31 $self->_private_state_reset;
635 9         15 $attr->{execute_returned} = $sth->execute( @{ $attr->{bind_values} } );
  9         39  
636 9         37 return $_ = $attr->{execute_returned};
637             }
638              
639             BEGIN {
640 9     9   1230 *execute = subname( execute => \&start );
641             }
642              
643             sub statement {
644 0     0 0 0 return $_STATEMENT;
645             }
646              
647             sub sth {
648 2206     2206 0 4341 return shift->_private_state->{sth};
649             }
650              
651             =head1 AUTHORS
652              
653             Iain Campbell Ecpanic@cpan.orgE
654              
655             =head1 COPYRIGHT AND LICENSE
656              
657             The DBIx::Squirrel module is Copyright (c) 2020-2025 Iain Campbell.
658             All rights reserved.
659              
660             You may distribute under the terms of either the GNU General Public
661             License or the Artistic License, as specified in the Perl 5.10.0 README file.
662              
663             =head1 SUPPORT / WARRANTY
664              
665             DBIx::Squirrel is free Open Source software. IT COMES WITHOUT WARRANTY OF ANY
666             KIND.
667              
668             =cut
669              
670             1;