e4f7d39b31faa4e2f82bb9bbf2f79d58aa91bf8e
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Cursor.pm
1 package DBIx::Class::Storage::DBI::Cursor;
2
3 use strict;
4 use warnings;
5
6 use base qw/DBIx::Class::Cursor/;
7
8 use Try::Tiny;
9 use namespace::clean;
10
11 __PACKAGE__->mk_group_accessors('simple' =>
12     qw/sth storage args pos attrs _dbh_gen/
13 );
14
15 =head1 NAME
16
17 DBIx::Class::Storage::DBI::Cursor - Object representing a query cursor on a
18 resultset.
19
20 =head1 SYNOPSIS
21
22   my $cursor = $schema->resultset('CD')->cursor();
23
24   # raw values off the database handle in resultset columns/select order
25   my @next_cd_column_values = $cursor->next;
26
27   # list of all raw values as arrayrefs
28   my @all_cds_column_values = $cursor->all;
29
30 =head1 DESCRIPTION
31
32 A Cursor represents a query cursor on a L<DBIx::Class::ResultSet> object. It
33 allows for traversing the result set with L</next>, retrieving all results with
34 L</all> and resetting the cursor with L</reset>.
35
36 Usually, you would use the cursor methods built into L<DBIx::Class::ResultSet>
37 to traverse it. See L<DBIx::Class::ResultSet/next>,
38 L<DBIx::Class::ResultSet/reset> and L<DBIx::Class::ResultSet/all> for more
39 information.
40
41 =head1 METHODS
42
43 =head2 new
44
45 Returns a new L<DBIx::Class::Storage::DBI::Cursor> object.
46
47 =cut
48
49 sub new {
50   my ($class, $storage, $args, $attrs) = @_;
51   $class = ref $class if ref $class;
52
53   my $new = {
54     storage => $storage,
55     args => $args,
56     pos => 0,
57     attrs => $attrs,
58     _dbh_gen => $storage->{_dbh_gen},
59   };
60
61   return bless ($new, $class);
62 }
63
64 =head2 next
65
66 =over 4
67
68 =item Arguments: none
69
70 =item Return Value: \@row_columns
71
72 =back
73
74 Advances the cursor to the next row and returns an array of column
75 values (the result of L<DBI/fetchrow_array> method).
76
77 =cut
78
79 sub _dbh_next {
80   my ($storage, $dbh, $self) = @_;
81
82   $self->_check_dbh_gen;
83   if (
84     $self->{attrs}{software_limit}
85       && $self->{attrs}{rows}
86         && $self->{pos} >= $self->{attrs}{rows}
87   ) {
88     $self->sth->finish if $self->sth->{Active};
89     $self->sth(undef);
90     $self->{done} = 1;
91   }
92   return if $self->{done};
93   unless ($self->sth) {
94     $self->sth(($storage->_select(@{$self->{args}}))[1]);
95     if ($self->{attrs}{software_limit}) {
96       if (my $offset = $self->{attrs}{offset}) {
97         $self->sth->fetch for 1 .. $offset;
98       }
99     }
100   }
101   my @row = $self->sth->fetchrow_array;
102   if (@row) {
103     $self->{pos}++;
104   } else {
105     $self->sth(undef);
106     $self->{done} = 1;
107   }
108   return @row;
109 }
110
111 sub next {
112   my ($self) = @_;
113   $self->{storage}->dbh_do($self->can('_dbh_next'), $self);
114 }
115
116 =head2 all
117
118 =over 4
119
120 =item Arguments: none
121
122 =item Return Value: \@row_columns+
123
124 =back
125
126 Returns a list of arrayrefs of column values for all rows in the
127 L<DBIx::Class::ResultSet>.
128
129 =cut
130
131 sub _dbh_all {
132   my ($storage, $dbh, $self) = @_;
133
134   $self->_check_dbh_gen;
135   $self->sth->finish if $self->sth && $self->sth->{Active};
136   $self->sth(undef);
137   my ($rv, $sth) = $storage->_select(@{$self->{args}});
138   return @{$sth->fetchall_arrayref};
139 }
140
141 sub all {
142   my ($self) = @_;
143   if ($self->{attrs}{software_limit}
144         && ($self->{attrs}{offset} || $self->{attrs}{rows})) {
145     return $self->next::method;
146   }
147
148   $self->{storage}->dbh_do($self->can('_dbh_all'), $self);
149 }
150
151 =head2 reset
152
153 Resets the cursor to the beginning of the L<DBIx::Class::ResultSet>.
154
155 =cut
156
157 sub reset {
158   my ($self) = @_;
159
160   # No need to care about failures here
161   try { $self->sth->finish }
162     if $self->sth && $self->sth->{Active};
163   $self->_soft_reset;
164   return undef;
165 }
166
167 sub _soft_reset {
168   my ($self) = @_;
169
170   $self->sth(undef);
171   delete $self->{done};
172   $self->{pos} = 0;
173 }
174
175 sub _check_dbh_gen {
176   my ($self) = @_;
177
178   if($self->{_dbh_gen} != $self->{storage}->{_dbh_gen}) {
179     $self->{_dbh_gen} = $self->{storage}->{_dbh_gen};
180     $self->_soft_reset;
181   }
182 }
183
184 sub DESTROY {
185   # None of the reasons this would die matter if we're in DESTROY anyways
186   if (my $sth = $_[0]->sth) {
187     local $SIG{__WARN__} = sub {};
188     try { $sth->finish } if $sth->FETCH('Active');
189   }
190 }
191
192 1;