File Coverage

blib/lib/DBIx/Class/Storage/DBI/Cursor.pm
Criterion Covered Total %
statement 76 86 88.3
branch 29 42 69.0
condition 19 29 65.5
subroutine 15 16 93.7
pod 4 5 80.0
total 143 178 80.3


line stmt bran cond sub pod time code
1             package DBIx::Class::Storage::DBI::Cursor;
2              
3 149     149   79132 use strict;
  149         368  
  149         4618  
4 149     149   799 use warnings;
  149         304  
  149         4942  
5              
6 149     149   825 use base 'DBIx::Class::Cursor';
  149         2319  
  149         54438  
7              
8 149     149   1003 use Scalar::Util qw(refaddr weaken);
  149         322  
  149         9093  
9 149     149   902 use DBIx::Class::_Util qw( detected_reinvoked_destructor dbic_internal_try );
  149         313  
  149         6316  
10 149     149   842 use namespace::clean;
  149         318  
  149         3320  
11              
12             __PACKAGE__->mk_group_accessors('simple' =>
13             qw/storage args attrs/
14             );
15              
16             =head1 NAME
17              
18             DBIx::Class::Storage::DBI::Cursor - Object representing a query cursor on a
19             resultset.
20              
21             =head1 SYNOPSIS
22              
23             my $cursor = $schema->resultset('CD')->cursor();
24              
25             # raw values off the database handle in resultset columns/select order
26             my @next_cd_column_values = $cursor->next;
27              
28             # list of all raw values as arrayrefs
29             my @all_cds_column_values = $cursor->all;
30              
31             =head1 DESCRIPTION
32              
33             A Cursor represents a query cursor on a L object. It
34             allows for traversing the result set with L, retrieving all results with
35             L and resetting the cursor with L.
36              
37             Usually, you would use the cursor methods built into L
38             to traverse it. See L,
39             L and L for more
40             information.
41              
42             =head1 METHODS
43              
44             =head2 new
45              
46             Returns a new L object.
47              
48             =cut
49              
50             {
51             my %cursor_registry;
52              
53             sub new {
54 3833     3833 1 13564 my ($class, $storage, $args, $attrs) = @_;
55              
56 3833   33     28103 my $self = bless {
57             storage => $storage,
58             args => $args,
59             attrs => $attrs,
60             }, ref $class || $class;
61              
62 3833         8343 if (DBIx::Class::_ENV_::HAS_ITHREADS) {
63              
64             # quick "garbage collection" pass - prevents the registry
65             # from slowly growing with a bunch of undef-valued keys
66             defined $cursor_registry{$_} or delete $cursor_registry{$_}
67             for keys %cursor_registry;
68              
69             weaken( $cursor_registry{ refaddr($self) } = $self )
70             }
71              
72 3833         31064 return $self;
73             }
74              
75             sub DBIx::Class::__DBI_Cursor_iThreads_handler__::CLONE {
76 0     0   0 for (keys %cursor_registry) {
77             # once marked we no longer care about them, hence no
78             # need to keep in the registry, left alone renumber the
79             # keys (all addresses are now different)
80 0 0       0 my $self = delete $cursor_registry{$_}
81             or next;
82              
83 0         0 $self->{_intra_thread} = 1;
84             }
85              
86             # Dummy NEXTSTATE ensuring the all temporaries on the stack are garbage
87             # collected before leaving this scope. Depending on the code above, this
88             # may very well be just a preventive measure guarding future modifications
89 0         0 undef;
90             }
91             }
92              
93             =head2 next
94              
95             =over 4
96              
97             =item Arguments: none
98              
99             =item Return Value: \@row_columns
100              
101             =back
102              
103             Advances the cursor to the next row and returns an array of column
104             values (the result of L method).
105              
106             =cut
107              
108             sub next {
109 6158     6158 1 11889 my $self = shift;
110              
111 6158 100       16770 return if $self->{_done};
112              
113 6141         11850 my $sth;
114              
115 6141 100 100     20488 if (
      100        
      100        
116             $self->{attrs}{software_limit}
117             && $self->{attrs}{rows}
118             && ($self->{_pos}||0) >= $self->{attrs}{rows}
119             ) {
120 2 50       6 if ($sth = $self->sth) {
121             # explicit finish will issue warnings, unlike the DESTROY below
122 2 50       21 $sth->finish if $sth->FETCH('Active');
123             }
124 2         7 $self->{_done} = 1;
125 2         8 return;
126             }
127              
128 6139 100       15782 unless ($sth = $self->sth) {
129 2726         9983 (undef, $sth, undef) = $self->storage->_select( @{$self->{args}} );
  2726         46955  
130              
131 2713         109060 $self->{_results} = [ (undef) x $sth->FETCH('NUM_OF_FIELDS') ];
132 2713         7956 $sth->bind_columns( \( @{$self->{_results}} ) );
  2713         23182  
133              
134 2713 100 100     100324 if ( $self->{attrs}{software_limit} and $self->{attrs}{offset} ) {
135 2         23 $sth->fetch for 1 .. $self->{attrs}{offset};
136             }
137              
138 2713         12203 $self->sth($sth);
139             }
140              
141 6126 100       72483 if ($sth->fetch) {
142 5286         14560 $self->{_pos}++;
143 5286         9484 return @{$self->{_results}};
  5286         33680  
144             } else {
145 840         2821 $self->{_done} = 1;
146 840         4422 return ();
147             }
148             }
149              
150              
151             =head2 all
152              
153             =over 4
154              
155             =item Arguments: none
156              
157             =item Return Value: \@row_columns+
158              
159             =back
160              
161             Returns a list of arrayrefs of column values for all rows in the
162             L.
163              
164             =cut
165              
166             sub all {
167 1711     1711 1 4018 my $self = shift;
168              
169             # delegate to DBIC::Cursor which will delegate back to next()
170 1711 50 33     6268 if ($self->{attrs}{software_limit}
      66        
171             && ($self->{attrs}{offset} || $self->{attrs}{rows})) {
172 1         4 return $self->next::method(@_);
173             }
174              
175 1710         3536 my $sth;
176              
177 1710 50       5039 if ($sth = $self->sth) {
178             # explicit finish will issue warnings, unlike the DESTROY below
179 0 0 0     0 $sth->finish if ( ! $self->{_done} and $sth->FETCH('Active') );
180 0         0 $self->sth(undef);
181             }
182              
183 1710         6236 (undef, $sth) = $self->storage->_select( @{$self->{args}} );
  1710         20610  
184              
185             (
186             DBIx::Class::_ENV_::SHUFFLE_UNORDERED_RESULTSETS
187             and
188             ! $self->{attrs}{order_by}
189             and
190             require List::Util
191             )
192             ? List::Util::shuffle( @{$sth->fetchall_arrayref} )
193 1702         48363 : @{$sth->fetchall_arrayref}
  1702         48417  
194             ;
195             }
196              
197             sub sth {
198 13716     13716 0 25051 my $self = shift;
199              
200 13716 100 66     50072 if (@_) {
    100          
201 5865         13328 delete @{$self}{qw/_pos _done _pid _intra_thread/};
  5865         18408  
202              
203 5865         15006 $self->{sth} = $_[0];
204 5865 100       22095 $self->{_pid} = $$ if ! DBIx::Class::_ENV_::BROKEN_FORK and $_[0];
205             }
206             elsif ($self->{sth} and ! $self->{_done}) {
207              
208 3415         5657 my $invalidate_handle_reason;
209              
210 3415 50       11748 if (DBIx::Class::_ENV_::HAS_ITHREADS and $self->{_intra_thread} ) {
211             $invalidate_handle_reason = 'Multi-thread';
212             }
213 0         0 elsif (!DBIx::Class::_ENV_::BROKEN_FORK and $self->{_pid} != $$ ) {
214 0         0 $invalidate_handle_reason = 'Multi-process';
215             }
216              
217 3415 50       7243 if ($invalidate_handle_reason) {
218             $self->storage->throw_exception("$invalidate_handle_reason access attempted while cursor in progress (position $self->{_pos})")
219 0 0       0 if $self->{_pos};
220              
221             # reinvokes the reset logic above
222 0         0 $self->sth(undef);
223             }
224             }
225              
226 13716         34923 return $self->{sth};
227             }
228              
229             =head2 reset
230              
231             Resets the cursor to the beginning of the L.
232              
233             =cut
234              
235             sub reset {
236 3152 100   3152 1 15592 $_[0]->__finish_sth if $_[0]->{sth};
237 3152         13691 $_[0]->sth(undef);
238              
239             # Dummy NEXTSTATE ensuring the all temporaries on the stack are garbage
240             # collected before leaving this scope. Depending on the code above, this
241             # may very well be just a preventive measure guarding future modifications
242 3152         7485 undef;
243             }
244              
245              
246             sub DESTROY {
247 3833 50   3833   1791133 return if &detected_reinvoked_destructor;
248              
249 3833 100       18139 $_[0]->__finish_sth if $_[0]->{sth};
250              
251             # Dummy NEXTSTATE ensuring the all temporaries on the stack are garbage
252             # collected before leaving this scope. Depending on the code above, this
253             # may very well be just a preventive measure guarding future modifications
254 3833         82635 undef;
255             }
256              
257             sub __finish_sth {
258             # It is (sadly) extremely important to finish() handles we are about
259             # to lose (due to reset() or a DESTROY() ). $rs->reset is the closest
260             # thing the user has to getting to the underlying finish() API and some
261             # DBDs mandate this (e.g. DBD::InterBase will segfault, DBD::Sybase
262             # won't start a transaction sanely, etc)
263             # We also can't use the accessor here, as it will trigger a fork/thread
264             # check, and resetting a cursor in a child is perfectly valid
265              
266 2713     2713   7508 my $self = shift;
267              
268             # No need to care about failures here
269             dbic_internal_try {
270 1871     1871   12252 local $SIG{__WARN__} = sub {};
271             $self->{sth}->finish
272 1871         21036 } if (
273             $self->{sth}
274             and
275             # weird double-negative to catch the case of ->FETCH throwing
276             # and attempt a finish *anyway*
277             ! dbic_internal_try {
278 2713     2713   23832 ! $self->{sth}->FETCH('Active')
279             }
280 2713 100 66     24257 );
281              
282             # Dummy NEXTSTATE ensuring the all temporaries on the stack are garbage
283             # collected before leaving this scope. Depending on the code above, this
284             # may very well be just a preventive measure guarding future modifications
285 2713         12382 undef;
286             }
287              
288             =head1 FURTHER QUESTIONS?
289              
290             Check the list of L.
291              
292             =head1 COPYRIGHT AND LICENSE
293              
294             This module is free software L
295             by the L. You can
296             redistribute it and/or modify it under the same terms as the
297             L.
298              
299             =cut
300              
301             1;