| Filename | /usr/share/perl5/DBIx/Class/Storage/DBI/Cursor.pm |
| Statements | Executed 34 statements in 178µs |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 2 | 2 | 2 | 203µs | 33.5ms | DBIx::Class::Storage::DBI::Cursor::next |
| 2 | 1 | 1 | 38µs | 43µs | DBIx::Class::Storage::DBI::Cursor::new |
| 2 | 1 | 1 | 36µs | 155µs | DBIx::Class::Storage::DBI::Cursor::__finish_sth |
| 1 | 1 | 1 | 35µs | 50µs | DBIx::Class::Storage::DBI::Cursor::BEGIN@10 |
| 4 | 2 | 1 | 25µs | 25µs | DBIx::Class::Storage::DBI::Cursor::sth |
| 1 | 1 | 1 | 21µs | 62µs | DBIx::Class::Storage::DBI::Cursor::BEGIN@8 |
| 1 | 1 | 1 | 20µs | 31µs | DBIx::Class::Storage::DBI::Cursor::BEGIN@3 |
| 1 | 1 | 1 | 19µs | 619µs | DBIx::Class::Storage::DBI::Cursor::BEGIN@6 |
| 2 | 2 | 2 | 18µs | 172µs | DBIx::Class::Storage::DBI::Cursor::DESTROY |
| 1 | 1 | 1 | 15µs | 42µs | DBIx::Class::Storage::DBI::Cursor::BEGIN@9 |
| 1 | 1 | 1 | 14µs | 199µs | DBIx::Class::Storage::DBI::Cursor::BEGIN@11 |
| 1 | 1 | 1 | 12µs | 19µs | DBIx::Class::Storage::DBI::Cursor::BEGIN@4 |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBI::Cursor::CLONE |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBI::Cursor::__ANON__[:251] |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBI::Cursor::__ANON__[:252] |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBI::Cursor::all |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBI::Cursor::reset |
| 0 | 0 | 0 | 0s | 0s | DBIx::Class::Storage::DBI::Cursor::try {...} |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package DBIx::Class::Storage::DBI::Cursor; | ||||
| 2 | |||||
| 3 | 2 | 43µs | # spent 31µs (20+12) within DBIx::Class::Storage::DBI::Cursor::BEGIN@3 which was called:
# once (20µs+12µs) by Class::C3::Componentised::ensure_class_loaded at line 3 # spent 31µs making 1 call to DBIx::Class::Storage::DBI::Cursor::BEGIN@3
# spent 12µs making 1 call to strict::import | ||
| 4 | 2 | 26µs | # spent 19µs (12+7) within DBIx::Class::Storage::DBI::Cursor::BEGIN@4 which was called:
# once (12µs+7µs) by Class::C3::Componentised::ensure_class_loaded at line 4 # spent 19µs making 1 call to DBIx::Class::Storage::DBI::Cursor::BEGIN@4
# spent 7µs making 1 call to warnings::import | ||
| 5 | |||||
| 6 | 2 | 1.22ms | # spent 619µs (19+601) within DBIx::Class::Storage::DBI::Cursor::BEGIN@6 which was called:
# once (19µs+601µs) by Class::C3::Componentised::ensure_class_loaded at line 6 # spent 619µs making 1 call to DBIx::Class::Storage::DBI::Cursor::BEGIN@6
# spent 601µs making 1 call to base::import | ||
| 7 | |||||
| 8 | 2 | 104µs | # spent 62µs (21+42) within DBIx::Class::Storage::DBI::Cursor::BEGIN@8 which was called:
# once (21µs+42µs) by Class::C3::Componentised::ensure_class_loaded at line 8 # spent 62µs making 1 call to DBIx::Class::Storage::DBI::Cursor::BEGIN@8
# spent 42µs making 1 call to Exporter::import | ||
| 9 | 2 | 68µs | # spent 42µs (15+27) within DBIx::Class::Storage::DBI::Cursor::BEGIN@9 which was called:
# once (15µs+27µs) by Class::C3::Componentised::ensure_class_loaded at line 9 # spent 42µs making 1 call to DBIx::Class::Storage::DBI::Cursor::BEGIN@9
# spent 27µs making 1 call to Exporter::import | ||
| 10 | 2 | 65µs | # spent 50µs (35+15) within DBIx::Class::Storage::DBI::Cursor::BEGIN@10 which was called:
# once (35µs+15µs) by Class::C3::Componentised::ensure_class_loaded at line 10 # spent 50µs making 1 call to DBIx::Class::Storage::DBI::Cursor::BEGIN@10
# spent 15µs making 1 call to List::Util::import | ||
| 11 | 2 | 383µs | # spent 199µs (14+185) within DBIx::Class::Storage::DBI::Cursor::BEGIN@11 which was called:
# once (14µs+185µs) by Class::C3::Componentised::ensure_class_loaded at line 11 # spent 199µs making 1 call to DBIx::Class::Storage::DBI::Cursor::BEGIN@11
# spent 185µs making 1 call to namespace::clean::import | ||
| 12 | |||||
| 13 | 1 | 242µs | __PACKAGE__->mk_group_accessors('simple' => # spent 242µs making 1 call to Class::Accessor::Grouped::mk_group_accessors | ||
| 14 | qw/storage args attrs/ | ||||
| 15 | ); | ||||
| 16 | |||||
| 17 | =head1 NAME | ||||
| 18 | |||||
| 19 | DBIx::Class::Storage::DBI::Cursor - Object representing a query cursor on a | ||||
| 20 | resultset. | ||||
| 21 | |||||
| 22 | =head1 SYNOPSIS | ||||
| 23 | |||||
| 24 | my $cursor = $schema->resultset('CD')->cursor(); | ||||
| 25 | |||||
| 26 | # raw values off the database handle in resultset columns/select order | ||||
| 27 | my @next_cd_column_values = $cursor->next; | ||||
| 28 | |||||
| 29 | # list of all raw values as arrayrefs | ||||
| 30 | my @all_cds_column_values = $cursor->all; | ||||
| 31 | |||||
| 32 | =head1 DESCRIPTION | ||||
| 33 | |||||
| 34 | A Cursor represents a query cursor on a L<DBIx::Class::ResultSet> object. It | ||||
| 35 | allows for traversing the result set with L</next>, retrieving all results with | ||||
| 36 | L</all> and resetting the cursor with L</reset>. | ||||
| 37 | |||||
| 38 | Usually, you would use the cursor methods built into L<DBIx::Class::ResultSet> | ||||
| 39 | to traverse it. See L<DBIx::Class::ResultSet/next>, | ||||
| 40 | L<DBIx::Class::ResultSet/reset> and L<DBIx::Class::ResultSet/all> for more | ||||
| 41 | information. | ||||
| 42 | |||||
| 43 | =head1 METHODS | ||||
| 44 | |||||
| 45 | =head2 new | ||||
| 46 | |||||
| 47 | Returns a new L<DBIx::Class::Storage::DBI::Cursor> object. | ||||
| 48 | |||||
| 49 | =cut | ||||
| 50 | |||||
| 51 | { | ||||
| 52 | my %cursor_registry; | ||||
| 53 | |||||
| 54 | # spent 43µs (38+5) within DBIx::Class::Storage::DBI::Cursor::new which was called 2 times, avg 22µs/call:
# 2 times (38µs+5µs) by DBIx::Class::Storage::DBI::select at line 2559 of DBIx/Class/Storage/DBI.pm, avg 22µs/call | ||||
| 55 | 1 | 700ns | my ($class, $storage, $args, $attrs) = @_; | ||
| 56 | |||||
| 57 | 1 | 4µs | my $self = bless { | ||
| 58 | storage => $storage, | ||||
| 59 | args => $args, | ||||
| 60 | attrs => $attrs, | ||||
| 61 | }, ref $class || $class; | ||||
| 62 | |||||
| 63 | 1 | 600ns | if (DBIx::Class::_ENV_::HAS_ITHREADS) { | ||
| 64 | |||||
| 65 | # quick "garbage collection" pass - prevents the registry | ||||
| 66 | # from slowly growing with a bunch of undef-valued keys | ||||
| 67 | defined $cursor_registry{$_} or delete $cursor_registry{$_} | ||||
| 68 | 1 | 5µs | for keys %cursor_registry; | ||
| 69 | |||||
| 70 | 1 | 11µs | 4 | 5µs | weaken( $cursor_registry{ refaddr($self) } = $self ) # spent 2µs making 2 calls to Scalar::Util::refaddr, avg 1µs/call
# spent 2µs making 2 calls to Scalar::Util::weaken, avg 1µs/call |
| 71 | } | ||||
| 72 | |||||
| 73 | 1 | 3µs | return $self; | ||
| 74 | } | ||||
| 75 | |||||
| 76 | sub CLONE { | ||||
| 77 | for (keys %cursor_registry) { | ||||
| 78 | # once marked we no longer care about them, hence no | ||||
| 79 | # need to keep in the registry, left alone renumber the | ||||
| 80 | # keys (all addresses are now different) | ||||
| 81 | my $self = delete $cursor_registry{$_} | ||||
| 82 | or next; | ||||
| 83 | |||||
| 84 | $self->{_intra_thread} = 1; | ||||
| 85 | } | ||||
| 86 | } | ||||
| 87 | } | ||||
| 88 | |||||
| 89 | =head2 next | ||||
| 90 | |||||
| 91 | =over 4 | ||||
| 92 | |||||
| 93 | =item Arguments: none | ||||
| 94 | |||||
| 95 | =item Return Value: \@row_columns | ||||
| 96 | |||||
| 97 | =back | ||||
| 98 | |||||
| 99 | Advances the cursor to the next row and returns an array of column | ||||
| 100 | values (the result of L<DBI/fetchrow_array> method). | ||||
| 101 | |||||
| 102 | =cut | ||||
| 103 | |||||
| 104 | # spent 33.5ms (203µs+33.3) within DBIx::Class::Storage::DBI::Cursor::next which was called 2 times, avg 16.7ms/call:
# once (140µs+32.0ms) by DBIx::Class::ResultSet::_construct_results at line 1329 of DBIx/Class/ResultSet.pm
# once (63µs+1.22ms) by DBIx::Class::ResultSetColumn::next at line 160 of DBIx/Class/ResultSetColumn.pm | ||||
| 105 | 1 | 500ns | my $self = shift; | ||
| 106 | |||||
| 107 | 1 | 1µs | return if $self->{_done}; | ||
| 108 | |||||
| 109 | 1 | 100ns | my $sth; | ||
| 110 | |||||
| 111 | 1 | 900ns | if ( | ||
| 112 | $self->{attrs}{software_limit} | ||||
| 113 | && $self->{attrs}{rows} | ||||
| 114 | && ($self->{_pos}||0) >= $self->{attrs}{rows} | ||||
| 115 | ) { | ||||
| 116 | if ($sth = $self->sth) { | ||||
| 117 | # explicit finish will issue warnings, unlike the DESTROY below | ||||
| 118 | $sth->finish if $sth->FETCH('Active'); | ||||
| 119 | } | ||||
| 120 | $self->{_done} = 1; | ||||
| 121 | return; | ||||
| 122 | } | ||||
| 123 | |||||
| 124 | 1 | 5µs | 2 | 7µs | unless ($sth = $self->sth) { # spent 7µs making 2 calls to DBIx::Class::Storage::DBI::Cursor::sth, avg 3µs/call |
| 125 | 1 | 27µs | 4 | 32.3ms | (undef, $sth, undef) = $self->storage->_select( @{$self->{args}} ); # spent 32.1ms making 2 calls to DBIx::Class::Storage::DBI::_select, avg 16.0ms/call
# spent 179µs making 2 calls to DBIx::Class::Storage::DBI::Cursor::storage, avg 90µs/call |
| 126 | |||||
| 127 | 1 | 14µs | 2 | 22µs | $self->{_results} = [ (undef) x $sth->FETCH('NUM_OF_FIELDS') ]; # spent 22µs making 2 calls to DBI::common::FETCH, avg 11µs/call |
| 128 | 1 | 7µs | 4 | 1.83ms | $sth->bind_columns( \( @{$self->{_results}} ) ); # spent 919µs making 2 calls to DBI::st::bind_columns, avg 459µs/call
# spent 908µs making 2 calls to DBD::mysql::st::__ANON__[DBD/mysql.pm:810], avg 454µs/call |
| 129 | |||||
| 130 | 1 | 1µs | if ( $self->{attrs}{software_limit} and $self->{attrs}{offset} ) { | ||
| 131 | $sth->fetch for 1 .. $self->{attrs}{offset}; | ||||
| 132 | } | ||||
| 133 | |||||
| 134 | 1 | 2µs | 2 | 18µs | $self->sth($sth); # spent 18µs making 2 calls to DBIx::Class::Storage::DBI::Cursor::sth, avg 9µs/call |
| 135 | } | ||||
| 136 | |||||
| 137 | 1 | 8µs | 2 | 14µs | if ($sth->fetch) { # spent 14µs making 2 calls to DBI::st::fetch, avg 7µs/call |
| 138 | 1 | 1µs | $self->{_pos}++; | ||
| 139 | 1 | 5µs | return @{$self->{_results}}; | ||
| 140 | } else { | ||||
| 141 | $self->{_done} = 1; | ||||
| 142 | return (); | ||||
| 143 | } | ||||
| 144 | } | ||||
| 145 | |||||
| 146 | |||||
| 147 | =head2 all | ||||
| 148 | |||||
| 149 | =over 4 | ||||
| 150 | |||||
| 151 | =item Arguments: none | ||||
| 152 | |||||
| 153 | =item Return Value: \@row_columns+ | ||||
| 154 | |||||
| 155 | =back | ||||
| 156 | |||||
| 157 | Returns a list of arrayrefs of column values for all rows in the | ||||
| 158 | L<DBIx::Class::ResultSet>. | ||||
| 159 | |||||
| 160 | =cut | ||||
| 161 | |||||
| 162 | sub all { | ||||
| 163 | my $self = shift; | ||||
| 164 | |||||
| 165 | # delegate to DBIC::Cursor which will delegate back to next() | ||||
| 166 | if ($self->{attrs}{software_limit} | ||||
| 167 | && ($self->{attrs}{offset} || $self->{attrs}{rows})) { | ||||
| 168 | return $self->next::method(@_); | ||||
| 169 | } | ||||
| 170 | |||||
| 171 | my $sth; | ||||
| 172 | |||||
| 173 | if ($sth = $self->sth) { | ||||
| 174 | # explicit finish will issue warnings, unlike the DESTROY below | ||||
| 175 | $sth->finish if ( ! $self->{_done} and $sth->FETCH('Active') ); | ||||
| 176 | $self->sth(undef); | ||||
| 177 | } | ||||
| 178 | |||||
| 179 | (undef, $sth) = $self->storage->_select( @{$self->{args}} ); | ||||
| 180 | |||||
| 181 | return ( | ||||
| 182 | DBIx::Class::_ENV_::SHUFFLE_UNORDERED_RESULTSETS | ||||
| 183 | and | ||||
| 184 | ! $self->{attrs}{order_by} | ||||
| 185 | ) | ||||
| 186 | ? shuffle @{$sth->fetchall_arrayref} | ||||
| 187 | : @{$sth->fetchall_arrayref} | ||||
| 188 | ; | ||||
| 189 | } | ||||
| 190 | |||||
| 191 | sub sth { | ||||
| 192 | 2 | 600ns | my $self = shift; | ||
| 193 | |||||
| 194 | 2 | 2µs | if (@_) { | ||
| 195 | 1 | 2µs | delete @{$self}{qw/_pos _done _pid _intra_thread/}; | ||
| 196 | |||||
| 197 | 1 | 1µs | $self->{sth} = $_[0]; | ||
| 198 | 1 | 2µs | $self->{_pid} = $$ if ! DBIx::Class::_ENV_::BROKEN_FORK and $_[0]; | ||
| 199 | } | ||||
| 200 | elsif ($self->{sth} and ! $self->{_done}) { | ||||
| 201 | |||||
| 202 | my $invalidate_handle_reason; | ||||
| 203 | |||||
| 204 | if (DBIx::Class::_ENV_::HAS_ITHREADS and $self->{_intra_thread} ) { | ||||
| 205 | $invalidate_handle_reason = 'Multi-thread'; | ||||
| 206 | } | ||||
| 207 | elsif (!DBIx::Class::_ENV_::BROKEN_FORK and $self->{_pid} != $$ ) { | ||||
| 208 | $invalidate_handle_reason = 'Multi-process'; | ||||
| 209 | } | ||||
| 210 | |||||
| 211 | if ($invalidate_handle_reason) { | ||||
| 212 | $self->storage->throw_exception("$invalidate_handle_reason access attempted while cursor in progress (position $self->{_pos})") | ||||
| 213 | if $self->{_pos}; | ||||
| 214 | |||||
| 215 | # reinvokes the reset logic above | ||||
| 216 | $self->sth(undef); | ||||
| 217 | } | ||||
| 218 | } | ||||
| 219 | |||||
| 220 | 2 | 9µs | return $self->{sth}; | ||
| 221 | } | ||||
| 222 | |||||
| 223 | =head2 reset | ||||
| 224 | |||||
| 225 | Resets the cursor to the beginning of the L<DBIx::Class::ResultSet>. | ||||
| 226 | |||||
| 227 | =cut | ||||
| 228 | |||||
| 229 | sub reset { | ||||
| 230 | $_[0]->__finish_sth if $_[0]->{sth}; | ||||
| 231 | $_[0]->sth(undef); | ||||
| 232 | } | ||||
| 233 | |||||
| 234 | |||||
| 235 | # spent 172µs (18+155) within DBIx::Class::Storage::DBI::Cursor::DESTROY which was called 2 times, avg 86µs/call:
# once (10µs+103µs) by DBIx::Class::ResultSet::count at line 32 of Koha/Borrower/Discharge.pm
# once (8µs+52µs) by CGI::Compile::ROOT::home_vagrant_kohaclone_circ_ysearch_2epl::__ANON__[/home/vagrant/kohaclone/circ/ysearch.pl:115] at line 2 of circ/ysearch.pl | ||||
| 236 | 1 | 10µs | 2 | 155µs | $_[0]->__finish_sth if $_[0]->{sth}; # spent 155µs making 2 calls to DBIx::Class::Storage::DBI::Cursor::__finish_sth, avg 78µs/call |
| 237 | } | ||||
| 238 | |||||
| 239 | # spent 155µs (36+119) within DBIx::Class::Storage::DBI::Cursor::__finish_sth which was called 2 times, avg 78µs/call:
# 2 times (36µs+119µs) by DBIx::Class::Storage::DBI::Cursor::DESTROY at line 236, avg 78µs/call | ||||
| 240 | # It is (sadly) extremely important to finish() handles we are about | ||||
| 241 | # to lose (due to reset() or a DESTROY() ). $rs->reset is the closest | ||||
| 242 | # thing the user has to getting to the underlying finish() API and some | ||||
| 243 | # DBDs mandate this (e.g. DBD::InterBase will segfault, DBD::Sybase | ||||
| 244 | # won't start a transaction sanely, etc) | ||||
| 245 | # We also can't use the accessor here, as it will trigger a fork/thread | ||||
| 246 | # check, and resetting a cursor in a child is perfectly valid | ||||
| 247 | |||||
| 248 | 1 | 500ns | my $self = shift; | ||
| 249 | |||||
| 250 | # No need to care about failures here | ||||
| 251 | 2 | 20µs | 1 | 3µs | try { local $SIG{__WARN__} = sub {}; $self->{sth}->finish } if ( # spent 3µs making 1 call to DBI::st::finish |
| 252 | 1 | 8µs | 2 | 7µs | $self->{sth} and ! try { ! $self->{sth}->FETCH('Active') } # spent 7µs making 2 calls to DBI::common::FETCH, avg 4µs/call |
| 253 | 1 | 26µs | 3 | 119µs | ); # spent 119µs making 3 calls to Try::Tiny::try, avg 40µs/call |
| 254 | } | ||||
| 255 | |||||
| 256 | =head1 FURTHER QUESTIONS? | ||||
| 257 | |||||
| 258 | Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>. | ||||
| 259 | |||||
| 260 | =head1 COPYRIGHT AND LICENSE | ||||
| 261 | |||||
| 262 | This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE> | ||||
| 263 | by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can | ||||
| 264 | redistribute it and/or modify it under the same terms as the | ||||
| 265 | L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>. | ||||
| 266 | |||||
| 267 | =cut | ||||
| 268 | |||||
| 269 | 1 | 179µs | 1; # spent 179µs making 1 call to B::Hooks::EndOfScope::XS::__ANON__ |