Attempt to reproduce reported mysql error (failed) - fixed another bug in ResultSetCo...
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSetColumn.pm
1 package DBIx::Class::ResultSetColumn;
2 use strict;
3 use warnings;
4 use base 'DBIx::Class';
5 use List::Util;
6
7 =head1 NAME
8
9   DBIx::Class::ResultSetColumn - helpful methods for messing
10   with a single column of the resultset
11
12 =head1 SYNOPSIS
13
14   $rs = $schema->resultset('CD')->search({ artist => 'Tool' });
15   $rs_column = $rs->get_column('year');
16   $max_year = $rs_column->max; #returns latest year
17
18 =head1 DESCRIPTION
19
20 A convenience class used to perform operations on a specific column of
21 a resultset.
22
23 =cut
24
25 =head1 METHODS
26
27 =head2 new
28
29   my $obj = DBIx::Class::ResultSetColumn->new($rs, $column);
30
31 Creates a new resultset column object from the resultset and column
32 passed as params. Used internally by L<DBIx::Class::ResultSet/get_column>.
33
34 =cut
35
36 sub new {
37   my ($class, $rs, $column) = @_;
38   $class = ref $class if ref $class;
39
40   $rs->throw_exception("column must be supplied") unless $column;
41
42   my $new_parent_rs = $rs->search_rs; # we don't want to mess up the original, so clone it
43
44   # prefetch causes additional columns to be fetched, but we can not just make a new
45   # rs via the _resolved_attrs trick - we need to retain the separation between
46   # +select/+as and select/as. At the same time we want to preserve any joins that the
47   # prefetch would otherwise generate.
48   my $init_attrs = $new_parent_rs->{attrs} ||= {};
49   delete $init_attrs->{collapse};
50   $init_attrs->{join} = $rs->_merge_attr( delete $init_attrs->{join}, delete $init_attrs->{prefetch} );
51
52   # If $column can be found in the 'as' list of the parent resultset, use the
53   # corresponding element of its 'select' list (to keep any custom column
54   # definition set up with 'select' or '+select' attrs), otherwise use $column
55   # (to create a new column definition on-the-fly).
56   my $attrs = $new_parent_rs->_resolved_attrs;
57
58   my $as_list = $attrs->{as} || [];
59   my $select_list = $attrs->{select} || [];
60   my $as_index = List::Util::first { ($as_list->[$_] || "") eq $column } 0..$#$as_list;
61   my $select = defined $as_index ? $select_list->[$as_index] : $column;
62
63   my $new = bless { _select => $select, _as => $column, _parent_resultset => $new_parent_rs }, $class;
64   return $new;
65 }
66
67 =head2 as_query (EXPERIMENTAL)
68
69 =over 4
70
71 =item Arguments: none
72
73 =item Return Value: \[ $sql, @bind ]
74
75 =back
76
77 Returns the SQL query and bind vars associated with the invocant.
78
79 This is generally used as the RHS for a subquery.
80
81 B<NOTE>: This feature is still experimental.
82
83 =cut
84
85 sub as_query { return shift->_resultset->as_query(@_) }
86
87 =head2 next
88
89 =over 4
90
91 =item Arguments: none
92
93 =item Return Value: $value
94
95 =back
96
97 Returns the next value of the column in the resultset (or C<undef> if
98 there is none).
99
100 Much like L<DBIx::Class::ResultSet/next> but just returning the 
101 one value.
102
103 =cut
104
105 sub next {
106   my $self = shift;
107   my ($row) = $self->_resultset->cursor->next;
108   return $row;
109 }
110
111 =head2 all
112
113 =over 4
114
115 =item Arguments: none
116
117 =item Return Value: @values
118
119 =back
120
121 Returns all values of the column in the resultset (or C<undef> if
122 there are none).
123
124 Much like L<DBIx::Class::ResultSet/all> but returns values rather
125 than row objects.
126
127 =cut
128
129 sub all {
130   my $self = shift;
131   return map { $_->[0] } $self->_resultset->cursor->all;
132 }
133
134 =head2 reset
135
136 =over 4
137
138 =item Arguments: none
139
140 =item Return Value: $self
141
142 =back
143
144 Resets the underlying resultset's cursor, so you can iterate through the
145 elements of the column again.
146
147 Much like L<DBIx::Class::ResultSet/reset>.
148
149 =cut
150
151 sub reset {
152   my $self = shift;
153   $self->_resultset->cursor->reset;
154   return $self;
155 }
156
157 =head2 first
158
159 =over 4
160
161 =item Arguments: none
162
163 =item Return Value: $value
164
165 =back
166
167 Resets the underlying resultset and returns the next value of the column in the
168 resultset (or C<undef> if there is none).
169
170 Much like L<DBIx::Class::ResultSet/first> but just returning the one value.
171
172 =cut
173
174 sub first {
175   my $self = shift;
176   my ($row) = $self->_resultset->cursor->reset->next;
177   return $row;
178 }
179
180 =head2 min
181
182 =over 4
183
184 =item Arguments: none
185
186 =item Return Value: $lowest_value
187
188 =back
189
190   my $first_year = $year_col->min();
191
192 Wrapper for ->func. Returns the lowest value of the column in the
193 resultset (or C<undef> if there are none).
194
195 =cut
196
197 sub min {
198   return shift->func('MIN');
199 }
200
201 =head2 min_rs
202
203 =over 4
204
205 =item Arguments: none
206
207 =item Return Value: $resultset
208
209 =back
210
211   my $rs = $year_col->min_rs();
212
213 Wrapper for ->func_rs for function MIN().
214
215 =cut
216
217 sub min_rs { return shift->func_rs('MIN') }
218
219 =head2 max
220
221 =over 4
222
223 =item Arguments: none
224
225 =item Return Value: $highest_value
226
227 =back
228
229   my $last_year = $year_col->max();
230
231 Wrapper for ->func. Returns the highest value of the column in the
232 resultset (or C<undef> if there are none).
233
234 =cut
235
236 sub max {
237   return shift->func('MAX');
238 }
239
240 =head2 max_rs
241
242 =over 4
243
244 =item Arguments: none
245
246 =item Return Value: $resultset
247
248 =back
249
250   my $rs = $year_col->max_rs();
251
252 Wrapper for ->func_rs for function MAX().
253
254 =cut
255
256 sub max_rs { return shift->func_rs('MAX') }
257
258 =head2 sum
259
260 =over 4
261
262 =item Arguments: none
263
264 =item Return Value: $sum_of_values
265
266 =back
267
268   my $total = $prices_col->sum();
269
270 Wrapper for ->func. Returns the sum of all the values in the column of
271 the resultset. Use on varchar-like columns at your own risk.
272
273 =cut
274
275 sub sum {
276   return shift->func('SUM');
277 }
278
279 =head2 sum_rs
280
281 =over 4
282
283 =item Arguments: none
284
285 =item Return Value: $resultset
286
287 =back
288
289   my $rs = $year_col->sum_rs();
290
291 Wrapper for ->func_rs for function SUM().
292
293 =cut
294
295 sub sum_rs { return shift->func_rs('SUM') }
296
297 =head2 func
298
299 =over 4
300
301 =item Arguments: $function
302
303 =item Return Value: $function_return_value
304
305 =back
306
307   $rs = $schema->resultset("CD")->search({});
308   $length = $rs->get_column('title')->func('LENGTH');
309
310 Runs a query using the function on the column and returns the
311 value. Produces the following SQL:
312
313   SELECT LENGTH( title ) FROM cd me
314
315 =cut
316
317 sub func {
318   my ($self,$function) = @_;
319   my $cursor = $self->func_rs($function)->cursor;
320   
321   if( wantarray ) {
322     return map { $_->[ 0 ] } $cursor->all;
323   }
324
325   return ( $cursor->next )[ 0 ];
326 }
327
328 =head2 func_rs
329
330 =over 4
331
332 =item Arguments: $function
333
334 =item Return Value: $resultset
335
336 =back
337
338 Creates the resultset that C<func()> uses to run its query.
339
340 =cut
341
342 sub func_rs {
343   my ($self,$function) = @_;
344   return $self->{_parent_resultset}->search(
345     undef, {
346       select => {$function => $self->{_select}},
347       as => [$self->{_as}],
348     },
349   );
350 }
351
352 =head2 throw_exception
353
354 See L<DBIx::Class::Schema/throw_exception> for details.
355   
356 =cut 
357     
358 sub throw_exception {
359   my $self=shift;
360   if (ref $self && $self->{_parent_resultset}) {
361     $self->{_parent_resultset}->throw_exception(@_)
362   } else {
363     croak(@_);
364   }
365 }
366
367 # _resultset
368 #
369 # Arguments: none
370 #
371 # Return Value: $resultset
372 #
373 #  $year_col->_resultset->next
374 #
375 # Returns the underlying resultset. Creates it from the parent resultset if
376 # necessary.
377
378 sub _resultset {
379   my $self = shift;
380
381   return $self->{_resultset} ||= $self->{_parent_resultset}->search(undef,
382     {
383       select => [$self->{_select}],
384       as => [$self->{_as}]
385     }
386   );
387 }
388
389 1;
390
391 =head1 AUTHORS
392
393 Luke Saunders <luke.saunders@gmail.com>
394
395 Jess Robinson
396
397 =head1 LICENSE
398
399 You may distribute this code under the same terms as Perl itself.
400
401 =cut