Hack around a stupid SQL::Abstract bug and add GROUP BY support
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet.pm
CommitLineData
89c0a5a2 1package DBIx::Class::ResultSet;
2
3use strict;
4use warnings;
5use overload
6 '0+' => 'count',
7 fallback => 1;
3c5b25c5 8use Data::Page;
89c0a5a2 9
ee38fa40 10=head1 NAME
11
bfab575a 12DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
ee38fa40 13
bfab575a 14=head1 SYNOPSIS
ee38fa40 15
bfab575a 16my $rs = MyApp::DB::Class->search(registered => 1);
17my @rows = MyApp::DB::Class->search(foo => 'bar');
ee38fa40 18
19=head1 DESCRIPTION
20
bfab575a 21The resultset is also known as an iterator. It is responsible for handling
22queries that may return an arbitrary number of rows, e.g. via C<search>
23or a C<has_many> relationship.
ee38fa40 24
25=head1 METHODS
26
976f3686 27=head2 new($source, \%$attrs)
ee38fa40 28
976f3686 29The resultset constructor. Takes a source object (usually a DBIx::Class::Table)
30and an attribute hash (see below for more information on attributes). Does
31not perform any queries -- these are executed as needed by the other methods.
ee38fa40 32
33=cut
34
89c0a5a2 35sub new {
cda04c3a 36 my ($class, $source, $attrs) = @_;
89c0a5a2 37 #use Data::Dumper; warn Dumper(@_);
2f5911b2 38 $class = ref $class if ref $class;
89c0a5a2 39 $attrs = { %{ $attrs || {} } };
c7ce65e6 40 my %seen;
976f3686 41 if (!$attrs->{select}) {
42 my @cols = ($attrs->{cols}
43 ? @{delete $attrs->{cols}}
44 : $source->result_class->_select_columns);
45 $attrs->{select} = [ map { m/\./ ? $_ : "me.$_" } @cols ];
46 }
47 $attrs->{as} ||= [ map { m/^me\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
48 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
cda04c3a 49 $attrs->{from} ||= [ { 'me' => $source->name } ];
fef5d100 50 if ($attrs->{join}) {
c7ce65e6 51 foreach my $j (ref $attrs->{join} eq 'ARRAY'
52 ? (@{$attrs->{join}}) : ($attrs->{join})) {
53 if (ref $j eq 'HASH') {
54 $seen{$_} = 1 foreach keys %$j;
55 } else {
56 $seen{$j} = 1;
57 }
58 }
cda04c3a 59 push(@{$attrs->{from}}, $source->result_class->_resolve_join($attrs->{join}, 'me'));
c7ce65e6 60 }
54540863 61 $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
c7ce65e6 62 foreach my $pre (@{$attrs->{prefetch} || []}) {
cda04c3a 63 push(@{$attrs->{from}}, $source->result_class->_resolve_join($pre, 'me'))
c7ce65e6 64 unless $seen{$pre};
976f3686 65 my @pre =
c7ce65e6 66 map { "$pre.$_" }
976f3686 67 $source->result_class->_relationships->{$pre}->{class}->columns;
68 push(@{$attrs->{select}}, @pre);
69 push(@{$attrs->{as}}, @pre);
fef5d100 70 }
89c0a5a2 71 my $new = {
cda04c3a 72 source => $source,
89c0a5a2 73 cond => $attrs->{where},
0a3c5b43 74 from => $attrs->{from},
3c5b25c5 75 count => undef,
76 pager => undef,
89c0a5a2 77 attrs => $attrs };
2f5911b2 78 bless ($new, $class);
976f3686 79 $new->pager if $attrs->{page};
9229f20a 80 return $new;
89c0a5a2 81}
82
bfab575a 83=head2 search
0a3c5b43 84
6009260a 85 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
86 my $new_rs = $rs->search({ foo => 3 });
87
88If you need to pass in additional attributes but no additional condition,
89call it as ->search(undef, \%attrs);
90
91 my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
0a3c5b43 92
93=cut
94
95sub search {
96 my $self = shift;
97
6009260a 98 #use Data::Dumper;warn Dumper(@_);
99
0a3c5b43 100 my $attrs = { %{$self->{attrs}} };
101 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
102 $attrs = { %{ pop(@_) } };
103 }
104
6009260a 105 my $where = ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_});
0a3c5b43 106 if (defined $where) {
107 $where = (defined $attrs->{where}
108 ? { '-and' => [ $where, $attrs->{where} ] }
109 : $where);
110 $attrs->{where} = $where;
111 }
112
113 my $rs = $self->new($self->{source}, $attrs);
114
115 return (wantarray ? $rs->all : $rs);
116}
117
bfab575a 118=head2 search_literal
6009260a 119 my @obj = $rs->search_literal($literal_where_cond, @bind);
120 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
121
122Pass a literal chunk of SQL to be added to the conditional part of the
123resultset
124
bfab575a 125=cut
126
6009260a 127sub search_literal {
128 my ($self, $cond, @vals) = @_;
129 my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
130 $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
131 return $self->search(\$cond, $attrs);
132}
0a3c5b43 133
bfab575a 134=head2 cursor
ee38fa40 135
bfab575a 136Returns a storage-driven cursor to the given resultset.
ee38fa40 137
138=cut
139
73f58123 140sub cursor {
141 my ($self) = @_;
2f5911b2 142 my ($source, $attrs) = @{$self}{qw/source attrs/};
3c5b25c5 143 if ($attrs->{page}) {
144 $attrs->{rows} = $self->pager->entries_per_page;
145 $attrs->{offset} = $self->pager->skipped;
146 }
73f58123 147 return $self->{cursor}
976f3686 148 ||= $source->storage->select($self->{from}, $attrs->{select},
73f58123 149 $attrs->{where},$attrs);
150}
151
bfab575a 152=head2 search_like
58a4bd18 153
154Identical to search except defaults to 'LIKE' instead of '=' in condition
155
156=cut
157
158sub search_like {
159 my $class = shift;
160 my $attrs = { };
161 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
162 $attrs = pop(@_);
163 }
164 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
165 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
166 return $class->search($query, { %$attrs });
167}
168
bfab575a 169=head2 slice($first, $last)
ee38fa40 170
bfab575a 171Returns a subset of elements from the resultset.
ee38fa40 172
173=cut
174
89c0a5a2 175sub slice {
176 my ($self, $min, $max) = @_;
177 my $attrs = { %{ $self->{attrs} || {} } };
cda04c3a 178 $self->{source}->result_class->throw("Can't slice without where") unless $attrs->{where};
89c0a5a2 179 $attrs->{offset} = $min;
180 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
2f5911b2 181 my $slice = $self->new($self->{source}, $attrs);
89c0a5a2 182 return (wantarray ? $slice->all : $slice);
183}
184
bfab575a 185=head2 next
ee38fa40 186
bfab575a 187Returns the next element in the resultset (undef is there is none).
ee38fa40 188
189=cut
190
89c0a5a2 191sub next {
192 my ($self) = @_;
73f58123 193 my @row = $self->cursor->next;
89c0a5a2 194 return unless (@row);
c7ce65e6 195 return $self->_construct_object(@row);
196}
197
198sub _construct_object {
199 my ($self, @row) = @_;
976f3686 200 my @cols = @{ $self->{attrs}{as} };
201 #warn "@cols -> @row";
3cbddeb1 202 @cols = grep { /\(/ or ! /\./ } @cols;
33ce49d6 203 my $new;
c7ce65e6 204 unless ($self->{attrs}{prefetch}) {
cda04c3a 205 $new = $self->{source}->result_class->_row_to_object(\@cols, \@row);
c7ce65e6 206 } else {
207 my @main = splice(@row, 0, scalar @cols);
cda04c3a 208 $new = $self->{source}->result_class->_row_to_object(\@cols, \@main);
c7ce65e6 209 PRE: foreach my $pre (@{$self->{attrs}{prefetch}}) {
cda04c3a 210 my $rel_obj = $self->{source}->result_class->_relationships->{$pre};
211 my $pre_class = $self->{source}->result_class->resolve_class($rel_obj->{class});
dd417d06 212 my @pre_cols = $pre_class->_select_columns;
c7ce65e6 213 my @vals = splice(@row, 0, scalar @pre_cols);
dd417d06 214 my $fetched = $pre_class->_row_to_object(\@pre_cols, \@vals);
cda04c3a 215 $self->{source}->result_class->throw("No accessor for prefetched $pre")
c7ce65e6 216 unless defined $rel_obj->{attrs}{accessor};
217 if ($rel_obj->{attrs}{accessor} eq 'single') {
218 foreach my $pri ($rel_obj->{class}->primary_columns) {
7cd300ea 219 unless (defined $fetched->get_column($pri)) {
220 undef $fetched;
221 last;
222 }
c7ce65e6 223 }
224 $new->{_relationship_data}{$pre} = $fetched;
225 } elsif ($rel_obj->{attrs}{accessor} eq 'filter') {
226 $new->{_inflated_column}{$pre} = $fetched;
227 } else {
cda04c3a 228 $self->{source}->result_class->throw("Don't know how to store prefetched $pre");
c7ce65e6 229 }
230 }
c7ce65e6 231 }
33ce49d6 232 $new = $self->{attrs}{record_filter}->($new)
233 if exists $self->{attrs}{record_filter};
234 return $new;
89c0a5a2 235}
236
bfab575a 237=head2 count
ee38fa40 238
bfab575a 239Performs an SQL C<COUNT> with the same query as the resultset was built
6009260a 240with to find the number of elements. If passed arguments, does a search
241on the resultset and counts the results of that.
ee38fa40 242
243=cut
244
89c0a5a2 245sub count {
6009260a 246 my $self = shift;
247 return $self->search(@_)->count if @_ && defined $_[0];
54540863 248 die "Unable to ->count with a GROUP BY" if defined $self->{attrs}{group_by};
3c5b25c5 249 unless ($self->{count}) {
976f3686 250 my $attrs = { %{ $self->{attrs} },
54540863 251 select => { 'count' => '*' },
252 as => [ 'count' ] };
976f3686 253 # offset and order by are not needed to count, page, join and prefetch
254 # will get in the way (add themselves to from again ...)
255 delete $attrs->{$_} for qw/offset order_by page join prefetch/;
3c5b25c5 256
257 my @cols = 'COUNT(*)';
976f3686 258 ($self->{count}) = $self->search(undef, $attrs)->cursor->next;
3c5b25c5 259 }
260 return 0 unless $self->{count};
9229f20a 261 return $self->{pager}->entries_on_this_page if ($self->{pager});
976f3686 262 return ( $self->{attrs}->{rows} && $self->{attrs}->{rows} < $self->{count} )
263 ? $self->{attrs}->{rows}
3c5b25c5 264 : $self->{count};
89c0a5a2 265}
266
bfab575a 267=head2 count_literal
6009260a 268
bfab575a 269Calls search_literal with the passed arguments, then count.
6009260a 270
271=cut
272
273sub count_literal { shift->search_literal(@_)->count; }
274
bfab575a 275=head2 all
ee38fa40 276
bfab575a 277Returns all elements in the resultset. Called implictly if the resultset
278is returned in list context.
ee38fa40 279
280=cut
281
89c0a5a2 282sub all {
283 my ($self) = @_;
c7ce65e6 284 return map { $self->_construct_object(@$_); }
73f58123 285 $self->cursor->all;
89c0a5a2 286}
287
bfab575a 288=head2 reset
ee38fa40 289
bfab575a 290Resets the resultset's cursor, so you can iterate through the elements again.
ee38fa40 291
292=cut
293
89c0a5a2 294sub reset {
295 my ($self) = @_;
73f58123 296 $self->cursor->reset;
89c0a5a2 297 return $self;
298}
299
bfab575a 300=head2 first
ee38fa40 301
bfab575a 302Resets the resultset and returns the first element.
ee38fa40 303
304=cut
305
89c0a5a2 306sub first {
307 return $_[0]->reset->next;
308}
309
bfab575a 310=head2 delete
ee38fa40 311
38659261 312Deletes all elements in the resultset.
ee38fa40 313
314=cut
315
28927b50 316sub delete {
89c0a5a2 317 my ($self) = @_;
318 $_->delete for $self->all;
319 return 1;
320}
321
28927b50 322*delete_all = \&delete; # Yeah, yeah, yeah ...
323
bfab575a 324=head2 pager
ee38fa40 325
326Returns a L<Data::Page> object for the current resultset. Only makes
327sense for queries with page turned on.
328
329=cut
330
3c5b25c5 331sub pager {
332 my ($self) = @_;
333 my $attrs = $self->{attrs};
334 delete $attrs->{offset};
335 my $rows_per_page = delete $attrs->{rows} || 10;
336 $self->{pager} ||= Data::Page->new(
337 $self->count, $rows_per_page, $attrs->{page} || 1);
338 $attrs->{rows} = $rows_per_page;
339 return $self->{pager};
340}
341
bfab575a 342=head2 page($page_num)
ee38fa40 343
bfab575a 344Returns a new resultset for the specified page.
ee38fa40 345
346=cut
347
3c5b25c5 348sub page {
349 my ($self, $page) = @_;
350 my $attrs = $self->{attrs};
351 $attrs->{page} = $page;
2f5911b2 352 return $self->new($self->{source}, $attrs);
3c5b25c5 353}
354
076652e8 355=head1 Attributes
356
bfab575a 357The resultset takes various attributes that modify its behavior.
358Here's an overview of them:
359
360=head2 order_by
076652e8 361
bfab575a 362Which column(s) to order the results by. This is currently passed
363through directly to SQL, so you can give e.g. C<foo DESC> for a
364descending order.
076652e8 365
976f3686 366=head2 cols (arrayref)
367
368Shortcut to request a particular set of columns to be retrieved - adds
369'me.' onto the start of any column without a '.' in it and sets 'select'
370from that, then auto-populates 'as' from 'select' as normal
371
372=head2 select (arrayref)
373
374Indicates which columns should be selected from the storage
375
376=head2 as (arrayref)
076652e8 377
976f3686 378Indicates column names for object inflation
ee38fa40 379
bfab575a 380=head2 join
ee38fa40 381
bfab575a 382Contains a list of relationships that should be joined for this query. Can also
383contain a hash reference to refer to that relation's relations. So, if one column
384in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
385C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
386If a foo contains many margles and you want to join those too, you can do
387C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
388related table as well, see C<prefetch> below.
ee38fa40 389
bfab575a 390=head2 prefetch
ee38fa40 391
bfab575a 392Contains a list of relationships that should be fetched along with the main
393query (when they are accessed afterwards they will have already been
394"prefetched"). This is useful for when you know you will need the related
395object(s), because it saves a query. Currently limited to prefetching
396one relationship deep, so unlike C<join>, prefetch must be an arrayref.
ee38fa40 397
bfab575a 398=head2 from
ee38fa40 399
bfab575a 400This attribute can contain a arrayref of elements. Each element can be another
ee38fa40 401arrayref, to nest joins, or it can be a hash which represents the two sides
402of the join.
403
bfab575a 404NOTE: Use this on your own risk. This allows you to shoot your foot off!
ee38fa40 405
bfab575a 406=head2 page
076652e8 407
bfab575a 408For a paged resultset, specifies which page to retrieve. Leave unset
409for an unpaged resultset.
076652e8 410
bfab575a 411=head2 rows
076652e8 412
bfab575a 413For a paged resultset, how many rows per page
076652e8 414
54540863 415=head2 group_by
416
417A list of columns to group by (note that 'count' doesn't work on grouped
418resultsets)
419
420=head2 distinct
421
422Set to 1 to group by all columns
423
bfab575a 424=cut
076652e8 425
89c0a5a2 4261;