Merge 'trunk' into 'DBIx-Class-current'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet.pm
1 package DBIx::Class::ResultSet;
2
3 use strict;
4 use warnings;
5 use overload
6         '0+'     => \&count,
7         'bool'   => sub { 1; },
8         fallback => 1;
9 use Data::Page;
10 use Storable;
11 use Scalar::Util qw/weaken/;
12
13 use DBIx::Class::ResultSetColumn;
14 use base qw/DBIx::Class/;
15 __PACKAGE__->load_components(qw/AccessorGroup/);
16 __PACKAGE__->mk_group_accessors('simple' => qw/result_source result_class/);
17
18 =head1 NAME
19
20 DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
21
22 =head1 SYNOPSIS
23
24   my $rs   = $schema->resultset('User')->search(registered => 1);
25   my @rows = $schema->resultset('CD')->search(year => 2005);
26
27 =head1 DESCRIPTION
28
29 The resultset is also known as an iterator. It is responsible for handling
30 queries that may return an arbitrary number of rows, e.g. via L</search>
31 or a C<has_many> relationship.
32
33 In the examples below, the following table classes are used:
34
35   package MyApp::Schema::Artist;
36   use base qw/DBIx::Class/;
37   __PACKAGE__->load_components(qw/Core/);
38   __PACKAGE__->table('artist');
39   __PACKAGE__->add_columns(qw/artistid name/);
40   __PACKAGE__->set_primary_key('artistid');
41   __PACKAGE__->has_many(cds => 'MyApp::Schema::CD');
42   1;
43
44   package MyApp::Schema::CD;
45   use base qw/DBIx::Class/;
46   __PACKAGE__->load_components(qw/Core/);
47   __PACKAGE__->table('cd');
48   __PACKAGE__->add_columns(qw/cdid artist title year/);
49   __PACKAGE__->set_primary_key('cdid');
50   __PACKAGE__->belongs_to(artist => 'MyApp::Schema::Artist');
51   1;
52
53 =head1 METHODS
54
55 =head2 new
56
57 =over 4
58
59 =item Arguments: $source, \%$attrs
60
61 =item Return Value: $rs
62
63 =back
64
65 The resultset constructor. Takes a source object (usually a
66 L<DBIx::Class::ResultSourceProxy::Table>) and an attribute hash (see
67 L</ATTRIBUTES> below).  Does not perform any queries -- these are
68 executed as needed by the other methods.
69
70 Generally you won't need to construct a resultset manually.  You'll
71 automatically get one from e.g. a L</search> called in scalar context:
72
73   my $rs = $schema->resultset('CD')->search({ title => '100th Window' });
74
75 IMPORTANT: If called on an object, proxies to new_result instead so
76
77   my $cd = $schema->resultset('CD')->new({ title => 'Spoon' });
78
79 will return a CD object, not a ResultSet.
80
81 =cut
82
83 sub new {
84   my $class = shift;
85   return $class->new_result(@_) if ref $class;
86   
87   my ($source, $attrs) = @_;
88   weaken $source;
89   $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } };
90   #use Data::Dumper; warn Dumper($attrs);
91   my $alias = ($attrs->{alias} ||= 'me');
92   
93   $attrs->{columns} ||= delete $attrs->{cols} if $attrs->{cols};
94   delete $attrs->{as} if $attrs->{columns};
95   $attrs->{columns} ||= [ $source->columns ] unless $attrs->{select};
96   $attrs->{select} = [
97     map { m/\./ ? $_ : "${alias}.$_" } @{delete $attrs->{columns}}
98   ] if $attrs->{columns};
99   $attrs->{as} ||= [
100     map { m/^\Q$alias.\E(.+)$/ ? $1 : $_ } @{$attrs->{select}}
101   ];
102   if (my $include = delete $attrs->{include_columns}) {
103     push(@{$attrs->{select}}, @$include);
104     push(@{$attrs->{as}}, map { m/([^.]+)$/; $1; } @$include);
105   }
106   #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
107
108   $attrs->{from} ||= [ { $alias => $source->from } ];
109   $attrs->{seen_join} ||= {};
110   my %seen;
111   if (my $join = delete $attrs->{join}) {
112     foreach my $j (ref $join eq 'ARRAY' ? @$join : ($join)) {
113       if (ref $j eq 'HASH') {
114         $seen{$_} = 1 foreach keys %$j;
115       } else {
116         $seen{$j} = 1;
117       }
118     }
119     push(@{$attrs->{from}}, $source->resolve_join(
120       $join, $attrs->{alias}, $attrs->{seen_join})
121     );
122   }
123   
124   $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
125   $attrs->{order_by} = [ $attrs->{order_by} ] if
126     $attrs->{order_by} and !ref($attrs->{order_by});
127   $attrs->{order_by} ||= [];
128
129   my $collapse = $attrs->{collapse} || {};
130   if (my $prefetch = delete $attrs->{prefetch}) {
131     my @pre_order;
132     foreach my $p (ref $prefetch eq 'ARRAY' ? @$prefetch : ($prefetch)) {
133       if ( ref $p eq 'HASH' ) {
134         foreach my $key (keys %$p) {
135           push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
136             unless $seen{$key};
137         }
138       } else {
139         push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
140             unless $seen{$p};
141       }
142       my @prefetch = $source->resolve_prefetch(
143            $p, $attrs->{alias}, {}, \@pre_order, $collapse);
144       push(@{$attrs->{select}}, map { $_->[0] } @prefetch);
145       push(@{$attrs->{as}}, map { $_->[1] } @prefetch);
146     }
147     push(@{$attrs->{order_by}}, @pre_order);
148   }
149   $attrs->{collapse} = $collapse;
150 #  use Data::Dumper; warn Dumper($collapse) if keys %{$collapse};
151
152   if ($attrs->{page}) {
153     $attrs->{rows} ||= 10;
154     $attrs->{offset} ||= 0;
155     $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
156   }
157
158   bless {
159     result_source => $source,
160     result_class => $attrs->{result_class} || $source->result_class,
161     cond => $attrs->{where},
162     from => $attrs->{from},
163     collapse => $collapse,
164     count => undef,
165     page => delete $attrs->{page},
166     pager => undef,
167     attrs => $attrs
168   }, $class;
169 }
170
171 =head2 search
172
173 =over 4
174
175 =item Arguments: $cond, \%attrs?
176
177 =item Return Value: $resultset (scalar context), @row_objs (list context)
178
179 =back
180
181   my @cds    = $cd_rs->search({ year => 2001 }); # "... WHERE year = 2001"
182   my $new_rs = $cd_rs->search({ year => 2005 });
183
184   my $new_rs = $cd_rs->search([ { year => 2005 }, { year => 2004 } ]);
185                  # year = 2005 OR year = 2004
186
187 If you need to pass in additional attributes but no additional condition,
188 call it as C<search(undef, \%attrs)>.
189
190   # "SELECT name, artistid FROM $artist_table"
191   my @all_artists = $schema->resultset('Artist')->search(undef, {
192     columns => [qw/name artistid/],
193   });
194
195 =cut
196
197 sub search {
198   my $self = shift;
199
200   my $rs;
201   if( @_ ) {
202     
203     my $attrs = { %{$self->{attrs}} };
204     my $having = delete $attrs->{having};
205     $attrs = { %$attrs, %{ pop(@_) } } if @_ > 1 and ref $_[$#_] eq 'HASH';
206
207     my $where = (@_
208                   ? ((@_ == 1 || ref $_[0] eq "HASH")
209                       ? shift
210                       : ((@_ % 2)
211                           ? $self->throw_exception(
212                               "Odd number of arguments to search")
213                           : {@_}))
214                   : undef());
215     if (defined $where) {
216       $attrs->{where} = (defined $attrs->{where}
217                 ? { '-and' =>
218                     [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
219                         $where, $attrs->{where} ] }
220                 : $where);
221     }
222
223     if (defined $having) {
224       $attrs->{having} = (defined $attrs->{having}
225                 ? { '-and' =>
226                     [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
227                         $having, $attrs->{having} ] }
228                 : $having);
229     }
230
231     $rs = (ref $self)->new($self->result_source, $attrs);
232   }
233   else {
234     $rs = $self;
235     $rs->reset;
236   }
237   return (wantarray ? $rs->all : $rs);
238 }
239
240 =head2 search_literal
241
242 =over 4
243
244 =item Arguments: $sql_fragment, @bind_values
245
246 =item Return Value: $resultset (scalar context), @row_objs (list context)
247
248 =back
249
250   my @cds   = $cd_rs->search_literal('year = ? AND title = ?', qw/2001 Reload/);
251   my $newrs = $artist_rs->search_literal('name = ?', 'Metallica');
252
253 Pass a literal chunk of SQL to be added to the conditional part of the
254 resultset query.
255
256 =cut
257
258 sub search_literal {
259   my ($self, $cond, @vals) = @_;
260   my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
261   $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
262   return $self->search(\$cond, $attrs);
263 }
264
265 =head2 find
266
267 =over 4
268
269 =item Arguments: @values | \%cols, \%attrs?
270
271 =item Return Value: $row_object
272
273 =back
274
275 Finds a row based on its primary key or unique constraint. For example:
276
277   my $cd = $schema->resultset('CD')->find(5);
278
279 Also takes an optional C<key> attribute, to search by a specific key or unique
280 constraint. For example:
281
282   my $cd = $schema->resultset('CD')->find(
283     {
284       artist => 'Massive Attack',
285       title  => 'Mezzanine',
286     },
287     { key => 'artist_title' }
288   );
289
290 See also L</find_or_create> and L</update_or_create>.
291
292 =cut
293
294 sub find {
295   my ($self, @vals) = @_;
296   my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
297
298   my @cols = $self->result_source->primary_columns;
299   if (exists $attrs->{key}) {
300     my %uniq = $self->result_source->unique_constraints;
301     $self->throw_exception(
302       "Unknown key $attrs->{key} on '" . $self->result_source->name . "'"
303     ) unless exists $uniq{$attrs->{key}};
304     @cols = @{ $uniq{$attrs->{key}} };
305   }
306   #use Data::Dumper; warn Dumper($attrs, @vals, @cols);
307   $self->throw_exception(
308     "Can't find unless a primary key or unique constraint is defined"
309   ) unless @cols;
310
311   my $query;
312   if (ref $vals[0] eq 'HASH') {
313     $query = { %{$vals[0]} };
314   } elsif (@cols == @vals) {
315     $query = {};
316     @{$query}{@cols} = @vals;
317   } else {
318     $query = {@vals};
319   }
320   foreach my $key (grep { ! m/\./ } keys %$query) {
321     $query->{"$self->{attrs}{alias}.$key"} = delete $query->{$key};
322   }
323   #warn Dumper($query);
324   
325   if (keys %$attrs) {
326       my $rs = $self->search($query,$attrs);
327       return keys %{$rs->{collapse}} ? $rs->next : $rs->single;
328   } else {
329       return keys %{$self->{collapse}} ?
330         $self->search($query)->next :
331         $self->single($query);
332   }
333 }
334
335 =head2 search_related
336
337 =over 4
338
339 =item Arguments: $cond, \%attrs?
340
341 =item Return Value: $new_resultset
342
343 =back
344
345   $new_rs = $cd_rs->search_related('artist', {
346     name => 'Emo-R-Us',
347   });
348
349 Searches the specified relationship, optionally specifying a condition and
350 attributes for matching records. See L</ATTRIBUTES> for more information.
351
352 =cut
353
354 sub search_related {
355   return shift->related_resultset(shift)->search(@_);
356 }
357
358 =head2 cursor
359
360 =over 4
361
362 =item Arguments: none
363
364 =item Return Value: $cursor
365
366 =back
367
368 Returns a storage-driven cursor to the given resultset. See
369 L<DBIx::Class::Cursor> for more information.
370
371 =cut
372
373 sub cursor {
374   my ($self) = @_;
375   my $attrs = { %{$self->{attrs}} };
376   return $self->{cursor}
377     ||= $self->result_source->storage->select($self->{from}, $attrs->{select},
378           $attrs->{where},$attrs);
379 }
380
381 =head2 single
382
383 =over 4
384
385 =item Arguments: $cond?
386
387 =item Return Value: $row_object?
388
389 =back
390
391   my $cd = $schema->resultset('CD')->single({ year => 2001 });
392
393 Inflates the first result without creating a cursor if the resultset has
394 any records in it; if not returns nothing. Used by find() as an optimisation.
395
396 =cut
397
398 sub single {
399   my ($self, $where) = @_;
400   my $attrs = { %{$self->{attrs}} };
401   if ($where) {
402     if (defined $attrs->{where}) {
403       $attrs->{where} = {
404         '-and' =>
405             [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
406                $where, delete $attrs->{where} ]
407       };
408     } else {
409       $attrs->{where} = $where;
410     }
411   }
412   my @data = $self->result_source->storage->select_single(
413           $self->{from}, $attrs->{select},
414           $attrs->{where},$attrs);
415   return (@data ? $self->_construct_object(@data) : ());
416 }
417
418 =head2 get_column
419
420 =over 4
421
422 =item Arguments: $cond?
423
424 =item Return Value: $resultsetcolumn
425
426 =back
427
428   my $max_length = $rs->get_column('length')->max;
429
430 Returns a ResultSetColumn instance for $column based on $self
431
432 =cut
433
434 sub get_column {
435   my ($self, $column) = @_;
436
437   my $new = DBIx::Class::ResultSetColumn->new($self, $column);
438   return $new;
439 }
440
441 =head2 search_like
442
443 =over 4
444
445 =item Arguments: $cond, \%attrs?
446
447 =item Return Value: $resultset (scalar context), @row_objs (list context)
448
449 =back
450
451   # WHERE title LIKE '%blue%'
452   $cd_rs = $rs->search_like({ title => '%blue%'});
453
454 Performs a search, but uses C<LIKE> instead of C<=> as the condition. Note
455 that this is simply a convenience method. You most likely want to use
456 L</search> with specific operators.
457
458 For more information, see L<DBIx::Class::Manual::Cookbook>.
459
460 =cut
461
462 sub search_like {
463   my $class = shift;
464   my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
465   my $query = ref $_[0] eq 'HASH' ? { %{shift()} }: {@_};
466   $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
467   return $class->search($query, { %$attrs });
468 }
469
470 =head2 slice
471
472 =over 4
473
474 =item Arguments: $first, $last
475
476 =item Return Value: $resultset (scalar context), @row_objs (list context)
477
478 =back
479
480 Returns a resultset or object list representing a subset of elements from the
481 resultset slice is called on. Indexes are from 0, i.e., to get the first
482 three records, call:
483
484   my ($one, $two, $three) = $rs->slice(0, 2);
485
486 =cut
487
488 sub slice {
489   my ($self, $min, $max) = @_;
490   my $attrs = {}; # = { %{ $self->{attrs} || {} } };
491   $attrs->{offset} = $self->{attrs}{offset} || 0;
492   $attrs->{offset} += $min;
493   $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
494   return $self->search(undef(), $attrs);
495   #my $slice = (ref $self)->new($self->result_source, $attrs);
496   #return (wantarray ? $slice->all : $slice);
497 }
498
499 =head2 next
500
501 =over 4
502
503 =item Arguments: none
504
505 =item Return Value: $result?
506
507 =back
508
509 Returns the next element in the resultset (C<undef> is there is none).
510
511 Can be used to efficiently iterate over records in the resultset:
512
513   my $rs = $schema->resultset('CD')->search;
514   while (my $cd = $rs->next) {
515     print $cd->title;
516   }
517
518 Note that you need to store the resultset object, and call C<next> on it. 
519 Calling C<< resultset('Table')->next >> repeatedly will always return the
520 first record from the resultset.
521
522 =cut
523
524 sub next {
525   my ($self) = @_;
526   if (@{$self->{all_cache} || []}) {
527     $self->{all_cache_position} ||= 0;
528     return $self->{all_cache}->[$self->{all_cache_position}++];
529   }
530   if ($self->{attrs}{cache}) {
531     $self->{all_cache_position} = 1;
532     return ($self->all)[0];
533   }
534   my @row = (exists $self->{stashed_row} ?
535                @{delete $self->{stashed_row}} :
536                $self->cursor->next
537   );
538 #  warn Dumper(\@row); use Data::Dumper;
539   return unless (@row);
540   return $self->_construct_object(@row);
541 }
542
543 sub _construct_object {
544   my ($self, @row) = @_;
545   my @as = @{ $self->{attrs}{as} };
546   
547   my $info = $self->_collapse_result(\@as, \@row);
548   
549   my $new = $self->result_class->inflate_result($self->result_source, @$info);
550   
551   $new = $self->{attrs}{record_filter}->($new)
552     if exists $self->{attrs}{record_filter};
553   return $new;
554 }
555
556 sub _collapse_result {
557   my ($self, $as, $row, $prefix) = @_;
558
559   my %const;
560
561   my @copy = @$row;
562   foreach my $this_as (@$as) {
563     my $val = shift @copy;
564     if (defined $prefix) {
565       if ($this_as =~ m/^\Q${prefix}.\E(.+)$/) {
566         my $remain = $1;
567         $remain =~ /^(?:(.*)\.)?([^.]+)$/;
568         $const{$1||''}{$2} = $val;
569       }
570     } else {
571       $this_as =~ /^(?:(.*)\.)?([^.]+)$/;
572       $const{$1||''}{$2} = $val;
573     }
574   }
575
576   my $info = [ {}, {} ];
577   foreach my $key (keys %const) {
578     if (length $key) {
579       my $target = $info;
580       my @parts = split(/\./, $key);
581       foreach my $p (@parts) {
582         $target = $target->[1]->{$p} ||= [];
583       }
584       $target->[0] = $const{$key};
585     } else {
586       $info->[0] = $const{$key};
587     }
588   }
589
590   my @collapse;
591   if (defined $prefix) {
592     @collapse = map {
593         m/^\Q${prefix}.\E(.+)$/ ? ($1) : ()
594     } keys %{$self->{collapse}}
595   } else {
596     @collapse = keys %{$self->{collapse}};
597   };
598
599   if (@collapse) {
600     my ($c) = sort { length $a <=> length $b } @collapse;
601     my $target = $info;
602     foreach my $p (split(/\./, $c)) {
603       $target = $target->[1]->{$p} ||= [];
604     }
605     my $c_prefix = (defined($prefix) ? "${prefix}.${c}" : $c);
606     my @co_key = @{$self->{collapse}{$c_prefix}};
607     my %co_check = map { ($_, $target->[0]->{$_}); } @co_key;
608     my $tree = $self->_collapse_result($as, $row, $c_prefix);
609     my (@final, @raw);
610     while ( !(grep {
611                 !defined($tree->[0]->{$_}) ||
612                 $co_check{$_} ne $tree->[0]->{$_}
613               } @co_key) ) {
614       push(@final, $tree);
615       last unless (@raw = $self->cursor->next);
616       $row = $self->{stashed_row} = \@raw;
617       $tree = $self->_collapse_result($as, $row, $c_prefix);
618       #warn Data::Dumper::Dumper($tree, $row);
619     }
620     @$target = @final;
621   }
622
623   return $info;
624 }
625
626 =head2 result_source
627
628 =over 4
629
630 =item Arguments: $result_source?
631
632 =item Return Value: $result_source
633
634 =back
635
636 An accessor for the primary ResultSource object from which this ResultSet
637 is derived.
638
639 =cut
640
641
642 =head2 count
643
644 =over 4
645
646 =item Arguments: $cond, \%attrs??
647
648 =item Return Value: $count
649
650 =back
651
652 Performs an SQL C<COUNT> with the same query as the resultset was built
653 with to find the number of elements. If passed arguments, does a search
654 on the resultset and counts the results of that.
655
656 Note: When using C<count> with C<group_by>, L<DBIX::Class> emulates C<GROUP BY>
657 using C<COUNT( DISTINCT( columns ) )>. Some databases (notably SQLite) do
658 not support C<DISTINCT> with multiple columns. If you are using such a
659 database, you should only use columns from the main table in your C<group_by>
660 clause.
661
662 =cut
663
664 sub count {
665   my $self = shift;
666   return $self->search(@_)->count if @_ and defined $_[0];
667   return scalar @{ $self->get_cache } if @{ $self->get_cache };
668
669   my $count = $self->_count;
670   return 0 unless $count;
671
672   $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
673   $count = $self->{attrs}{rows} if
674     $self->{attrs}{rows} and $self->{attrs}{rows} < $count;
675   return $count;
676 }
677
678 sub _count { # Separated out so pager can get the full count
679   my $self = shift;
680   my $select = { count => '*' };
681   my $attrs = { %{ $self->{attrs} } };
682   if (my $group_by = delete $attrs->{group_by}) {
683     delete $attrs->{having};
684     my @distinct = (ref $group_by ?  @$group_by : ($group_by));
685     # todo: try CONCAT for multi-column pk
686     my @pk = $self->result_source->primary_columns;
687     if (@pk == 1) {
688       foreach my $column (@distinct) {
689         if ($column =~ qr/^(?:\Q$attrs->{alias}.\E)?$pk[0]$/) {
690           @distinct = ($column);
691           last;
692         }
693       }
694     }
695
696     $select = { count => { distinct => \@distinct } };
697     #use Data::Dumper; die Dumper $select;
698   }
699
700   $attrs->{select} = $select;
701   $attrs->{as} = [qw/count/];
702
703   # offset, order by and page are not needed to count. record_filter is cdbi
704   delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
705         
706   my ($count) = (ref $self)->new($self->result_source, $attrs)->cursor->next;
707   return $count;
708 }
709
710 =head2 count_literal
711
712 =over 4
713
714 =item Arguments: $sql_fragment, @bind_values
715
716 =item Return Value: $count
717
718 =back
719
720 Counts the results in a literal query. Equivalent to calling L</search_literal>
721 with the passed arguments, then L</count>.
722
723 =cut
724
725 sub count_literal { shift->search_literal(@_)->count; }
726
727 =head2 all
728
729 =over 4
730
731 =item Arguments: none
732
733 =item Return Value: @objects
734
735 =back
736
737 Returns all elements in the resultset. Called implicitly if the resultset
738 is returned in list context.
739
740 =cut
741
742 sub all {
743   my ($self) = @_;
744   return @{ $self->get_cache } if @{ $self->get_cache };
745
746   my @obj;
747
748   if (keys %{$self->{collapse}}) {
749       # Using $self->cursor->all is really just an optimisation.
750       # If we're collapsing has_many prefetches it probably makes
751       # very little difference, and this is cleaner than hacking
752       # _construct_object to survive the approach
753     $self->cursor->reset;
754     my @row = $self->cursor->next;
755     while (@row) {
756       push(@obj, $self->_construct_object(@row));
757       @row = (exists $self->{stashed_row}
758                ? @{delete $self->{stashed_row}}
759                : $self->cursor->next);
760     }
761   } else {
762     @obj = map { $self->_construct_object(@$_) } $self->cursor->all;
763   }
764
765   $self->set_cache(\@obj) if $self->{attrs}{cache};
766   return @obj;
767 }
768
769 =head2 reset
770
771 =over 4
772
773 =item Arguments: none
774
775 =item Return Value: $self
776
777 =back
778
779 Resets the resultset's cursor, so you can iterate through the elements again.
780
781 =cut
782
783 sub reset {
784   my ($self) = @_;
785   $self->{all_cache_position} = 0;
786   $self->cursor->reset;
787   return $self;
788 }
789
790 =head2 first
791
792 =over 4
793
794 =item Arguments: none
795
796 =item Return Value: $object?
797
798 =back
799
800 Resets the resultset and returns an object for the first result (if the
801 resultset returns anything).
802
803 =cut
804
805 sub first {
806   return $_[0]->reset->next;
807 }
808
809 # _cond_for_update_delete
810 #
811 # update/delete require the condition to be modified to handle
812 # the differing SQL syntax available.  This transforms the $self->{cond}
813 # appropriately, returning the new condition.
814
815 sub _cond_for_update_delete {
816   my ($self) = @_;
817   my $cond = {};
818
819   if (!ref($self->{cond})) {
820     # No-op. No condition, we're updating/deleting everything
821   }
822   elsif (ref $self->{cond} eq 'ARRAY') {
823     $cond = [
824       map {
825         my %hash;
826         foreach my $key (keys %{$_}) {
827           $key =~ /([^.]+)$/;
828           $hash{$1} = $_->{$key};
829         }
830         \%hash;
831       } @{$self->{cond}}
832     ];
833   }
834   elsif (ref $self->{cond} eq 'HASH') {
835     if ((keys %{$self->{cond}})[0] eq '-and') {
836       $cond->{-and} = [];
837
838       my @cond = @{$self->{cond}{-and}};
839       for (my $i = 0; $i < @cond - 1; $i++) {
840         my $entry = $cond[$i];
841
842         my %hash;
843         if (ref $entry eq 'HASH') {
844           foreach my $key (keys %{$entry}) {
845             $key =~ /([^.]+)$/;
846             $hash{$1} = $entry->{$key};
847           }
848         }
849         else {
850           $entry =~ /([^.]+)$/;
851           $hash{$entry} = $cond[++$i];
852         }
853
854         push @{$cond->{-and}}, \%hash;
855       }
856     }
857     else {
858       foreach my $key (keys %{$self->{cond}}) {
859         $key =~ /([^.]+)$/;
860         $cond->{$1} = $self->{cond}{$key};
861       }
862     }
863   }
864   else {
865     $self->throw_exception(
866       "Can't update/delete on resultset with condition unless hash or array"
867     );
868   }
869
870   return $cond;
871 }
872
873
874 =head2 update
875
876 =over 4
877
878 =item Arguments: \%values
879
880 =item Return Value: $storage_rv
881
882 =back
883
884 Sets the specified columns in the resultset to the supplied values in a
885 single query. Return value will be true if the update succeeded or false
886 if no records were updated; exact type of success value is storage-dependent.
887
888 =cut
889
890 sub update {
891   my ($self, $values) = @_;
892   $self->throw_exception("Values for update must be a hash")
893     unless ref $values eq 'HASH';
894
895   my $cond = $self->_cond_for_update_delete;
896
897   return $self->result_source->storage->update(
898     $self->result_source->from, $values, $cond
899   );
900 }
901
902 =head2 update_all
903
904 =over 4
905
906 =item Arguments: \%values
907
908 =item Return Value: 1
909
910 =back
911
912 Fetches all objects and updates them one at a time. Note that C<update_all>
913 will run DBIC cascade triggers, while L</update> will not.
914
915 =cut
916
917 sub update_all {
918   my ($self, $values) = @_;
919   $self->throw_exception("Values for update must be a hash")
920     unless ref $values eq 'HASH';
921   foreach my $obj ($self->all) {
922     $obj->set_columns($values)->update;
923   }
924   return 1;
925 }
926
927 =head2 delete
928
929 =over 4
930
931 =item Arguments: none
932
933 =item Return Value: 1
934
935 =back
936
937 Deletes the contents of the resultset from its result source. Note that this
938 will not run DBIC cascade triggers. See L</delete_all> if you need triggers
939 to run.
940
941 =cut
942
943 sub delete {
944   my ($self) = @_;
945   my $del = {};
946
947   my $cond = $self->_cond_for_update_delete;
948
949   $self->result_source->storage->delete($self->result_source->from, $cond);
950   return 1;
951 }
952
953 =head2 delete_all
954
955 =over 4
956
957 =item Arguments: none
958
959 =item Return Value: 1
960
961 =back
962
963 Fetches all objects and deletes them one at a time. Note that C<delete_all>
964 will run DBIC cascade triggers, while L</delete> will not.
965
966 =cut
967
968 sub delete_all {
969   my ($self) = @_;
970   $_->delete for $self->all;
971   return 1;
972 }
973
974 =head2 pager
975
976 =over 4
977
978 =item Arguments: none
979
980 =item Return Value: $pager
981
982 =back
983
984 Return Value a L<Data::Page> object for the current resultset. Only makes
985 sense for queries with a C<page> attribute.
986
987 =cut
988
989 sub pager {
990   my ($self) = @_;
991   my $attrs = $self->{attrs};
992   $self->throw_exception("Can't create pager for non-paged rs")
993     unless $self->{page};
994   $attrs->{rows} ||= 10;
995   return $self->{pager} ||= Data::Page->new(
996     $self->_count, $attrs->{rows}, $self->{page});
997 }
998
999 =head2 page
1000
1001 =over 4
1002
1003 =item Arguments: $page_number
1004
1005 =item Return Value: $rs
1006
1007 =back
1008
1009 Returns a resultset for the $page_number page of the resultset on which page
1010 is called, where each page contains a number of rows equal to the 'rows'
1011 attribute set on the resultset (10 by default).
1012
1013 =cut
1014
1015 sub page {
1016   my ($self, $page) = @_;
1017   my $attrs = { %{$self->{attrs}} };
1018   $attrs->{page} = $page;
1019   return (ref $self)->new($self->result_source, $attrs);
1020 }
1021
1022 =head2 new_result
1023
1024 =over 4
1025
1026 =item Arguments: \%vals
1027
1028 =item Return Value: $object
1029
1030 =back
1031
1032 Creates an object in the resultset's result class and returns it.
1033
1034 =cut
1035
1036 sub new_result {
1037   my ($self, $values) = @_;
1038   $self->throw_exception( "new_result needs a hash" )
1039     unless (ref $values eq 'HASH');
1040   $self->throw_exception(
1041     "Can't abstract implicit construct, condition not a hash"
1042   ) if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
1043   my %new = %$values;
1044   my $alias = $self->{attrs}{alias};
1045   foreach my $key (keys %{$self->{cond}||{}}) {
1046     $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:\Q${alias}.\E)?([^.]+)$/);
1047   }
1048   my $obj = $self->result_class->new(\%new);
1049   $obj->result_source($self->result_source) if $obj->can('result_source');
1050   return $obj;
1051 }
1052
1053 =head2 create
1054
1055 =over 4
1056
1057 =item Arguments: \%vals
1058
1059 =item Return Value: $object
1060
1061 =back
1062
1063 Inserts a record into the resultset and returns the object representing it.
1064
1065 Effectively a shortcut for C<< ->new_result(\%vals)->insert >>.
1066
1067 =cut
1068
1069 sub create {
1070   my ($self, $attrs) = @_;
1071   $self->throw_exception( "create needs a hashref" )
1072     unless ref $attrs eq 'HASH';
1073   return $self->new_result($attrs)->insert;
1074 }
1075
1076 =head2 find_or_create
1077
1078 =over 4
1079
1080 =item Arguments: \%vals, \%attrs?
1081
1082 =item Return Value: $object
1083
1084 =back
1085
1086   $class->find_or_create({ key => $val, ... });
1087
1088 Searches for a record matching the search condition; if it doesn't find one,
1089 creates one and returns that instead.
1090
1091   my $cd = $schema->resultset('CD')->find_or_create({
1092     cdid   => 5,
1093     artist => 'Massive Attack',
1094     title  => 'Mezzanine',
1095     year   => 2005,
1096   });
1097
1098 Also takes an optional C<key> attribute, to search by a specific key or unique
1099 constraint. For example:
1100
1101   my $cd = $schema->resultset('CD')->find_or_create(
1102     {
1103       artist => 'Massive Attack',
1104       title  => 'Mezzanine',
1105     },
1106     { key => 'artist_title' }
1107   );
1108
1109 See also L</find> and L</update_or_create>.
1110
1111 =cut
1112
1113 sub find_or_create {
1114   my $self     = shift;
1115   my $attrs    = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
1116   my $hash     = ref $_[0] eq 'HASH' ? shift : {@_};
1117   my $exists   = $self->find($hash, $attrs);
1118   return defined $exists ? $exists : $self->create($hash);
1119 }
1120
1121 =head2 update_or_create
1122
1123 =over 4
1124
1125 =item Arguments: \%col_values, { key => $unique_constraint }?
1126
1127 =item Return Value: $object
1128
1129 =back
1130
1131   $class->update_or_create({ col => $val, ... });
1132
1133 First, searches for an existing row matching one of the unique constraints
1134 (including the primary key) on the source of this resultset. If a row is
1135 found, updates it with the other given column values. Otherwise, creates a new
1136 row.
1137
1138 Takes an optional C<key> attribute to search on a specific unique constraint.
1139 For example:
1140
1141   # In your application
1142   my $cd = $schema->resultset('CD')->update_or_create(
1143     {
1144       artist => 'Massive Attack',
1145       title  => 'Mezzanine',
1146       year   => 1998,
1147     },
1148     { key => 'artist_title' }
1149   );
1150
1151 If no C<key> is specified, it searches on all unique constraints defined on the
1152 source, including the primary key.
1153
1154 If the C<key> is specified as C<primary>, it searches only on the primary key.
1155
1156 See also L</find> and L</find_or_create>.
1157
1158 =cut
1159
1160 sub update_or_create {
1161   my $self = shift;
1162   my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
1163   my $hash = ref $_[0] eq 'HASH' ? shift : {@_};
1164
1165   my %unique_constraints = $self->result_source->unique_constraints;
1166   my @constraint_names   = (exists $attrs->{key}
1167                             ? ($attrs->{key})
1168                             : keys %unique_constraints);
1169
1170   my @unique_hashes;
1171   foreach my $name (@constraint_names) {
1172     my @unique_cols = @{ $unique_constraints{$name} };
1173     my %unique_hash =
1174       map  { $_ => $hash->{$_} }
1175       grep { exists $hash->{$_} }
1176       @unique_cols;
1177
1178     push @unique_hashes, \%unique_hash
1179       if (scalar keys %unique_hash == scalar @unique_cols);
1180   }
1181
1182   if (@unique_hashes) {
1183     my $row = $self->single(\@unique_hashes);
1184     if (defined $row) {
1185       $row->set_columns($hash);
1186       $row->update;
1187       return $row;
1188     }
1189   }
1190
1191   return $self->create($hash);
1192 }
1193
1194 =head2 get_cache
1195
1196 =over 4
1197
1198 =item Arguments: none
1199
1200 =item Return Value: \@cache_objects?
1201
1202 =back
1203
1204 Gets the contents of the cache for the resultset, if the cache is set.
1205
1206 =cut
1207
1208 sub get_cache {
1209   shift->{all_cache} || [];
1210 }
1211
1212 =head2 set_cache
1213
1214 =over 4
1215
1216 =item Arguments: \@cache_objects
1217
1218 =item Return Value: \@cache_objects
1219
1220 =back
1221
1222 Sets the contents of the cache for the resultset. Expects an arrayref
1223 of objects of the same class as those produced by the resultset. Note that
1224 if the cache is set the resultset will return the cached objects rather
1225 than re-querying the database even if the cache attr is not set.
1226
1227 =cut
1228
1229 sub set_cache {
1230   my ( $self, $data ) = @_;
1231   $self->throw_exception("set_cache requires an arrayref")
1232     if ref $data ne 'ARRAY';
1233   my $result_class = $self->result_class;
1234   foreach( @$data ) {
1235     $self->throw_exception(
1236       "cannot cache object of type '$_', expected '$result_class'"
1237     ) if ref $_ ne $result_class;
1238   }
1239   $self->{all_cache} = $data;
1240 }
1241
1242 =head2 clear_cache
1243
1244 =over 4
1245
1246 =item Arguments: none
1247
1248 =item Return Value: []
1249
1250 =back
1251
1252 Clears the cache for the resultset.
1253
1254 =cut
1255
1256 sub clear_cache {
1257   shift->set_cache([]);
1258 }
1259
1260 =head2 related_resultset
1261
1262 =over 4
1263
1264 =item Arguments: $relationship_name
1265
1266 =item Return Value: $resultset
1267
1268 =back
1269
1270 Returns a related resultset for the supplied relationship name.
1271
1272   $artist_rs = $schema->resultset('CD')->related_resultset('Artist');
1273
1274 =cut
1275
1276 sub related_resultset {
1277   my ( $self, $rel ) = @_;
1278   $self->{related_resultsets} ||= {};
1279   return $self->{related_resultsets}{$rel} ||= do {
1280       #warn "fetching related resultset for rel '$rel'";
1281       my $rel_obj = $self->result_source->relationship_info($rel);
1282       $self->throw_exception(
1283         "search_related: result source '" . $self->result_source->name .
1284         "' has no such relationship ${rel}")
1285         unless $rel_obj; #die Dumper $self->{attrs};
1286
1287       my $rs = $self->search(undef, { join => $rel });
1288       my $alias = defined $rs->{attrs}{seen_join}{$rel}
1289                     && $rs->{attrs}{seen_join}{$rel} > 1
1290                   ? join('_', $rel, $rs->{attrs}{seen_join}{$rel})
1291                   : $rel;
1292
1293       $self->result_source->schema->resultset($rel_obj->{class}
1294            )->search( undef,
1295              { %{$rs->{attrs}},
1296                alias => $alias,
1297                select => undef,
1298                as => undef }
1299            );
1300   };
1301 }
1302
1303 =head2 throw_exception
1304
1305 See L<DBIx::Class::Schema/throw_exception> for details.
1306
1307 =cut
1308
1309 sub throw_exception {
1310   my $self=shift;
1311   $self->result_source->schema->throw_exception(@_);
1312 }
1313
1314 # XXX: FIXME: Attributes docs need clearing up
1315
1316 =head1 ATTRIBUTES
1317
1318 The resultset takes various attributes that modify its behavior. Here's an
1319 overview of them:
1320
1321 =head2 order_by
1322
1323 =over 4
1324
1325 =item Value: ($order_by | \@order_by)
1326
1327 =back
1328
1329 Which column(s) to order the results by. This is currently passed
1330 through directly to SQL, so you can give e.g. C<year DESC> for a
1331 descending order on the column `year'.
1332
1333 =head2 columns
1334
1335 =over 4
1336
1337 =item Value: \@columns
1338
1339 =back
1340
1341 Shortcut to request a particular set of columns to be retrieved.  Adds
1342 C<me.> onto the start of any column without a C<.> in it and sets C<select>
1343 from that, then auto-populates C<as> from C<select> as normal. (You may also
1344 use the C<cols> attribute, as in earlier versions of DBIC.)
1345
1346 =head2 include_columns
1347
1348 =over 4
1349
1350 =item Value: \@columns
1351
1352 =back
1353
1354 Shortcut to include additional columns in the returned results - for example
1355
1356   $schema->resultset('CD')->search(undef, {
1357     include_columns => ['artist.name'],
1358     join => ['artist']
1359   });
1360
1361 would return all CDs and include a 'name' column to the information
1362 passed to object inflation
1363
1364 =head2 select
1365
1366 =over 4
1367
1368 =item Value: \@select_columns
1369
1370 =back
1371
1372 Indicates which columns should be selected from the storage. You can use
1373 column names, or in the case of RDBMS back ends, function or stored procedure
1374 names:
1375
1376   $rs = $schema->resultset('Employee')->search(undef, {
1377     select => [
1378       'name',
1379       { count => 'employeeid' },
1380       { sum => 'salary' }
1381     ]
1382   });
1383
1384 When you use function/stored procedure names and do not supply an C<as>
1385 attribute, the column names returned are storage-dependent. E.g. MySQL would
1386 return a column named C<count(employeeid)> in the above example.
1387
1388 =head2 as
1389
1390 =over 4
1391
1392 =item Value: \@inflation_names
1393
1394 =back
1395
1396 Indicates column names for object inflation. This is used in conjunction with
1397 C<select>, usually when C<select> contains one or more function or stored
1398 procedure names:
1399
1400   $rs = $schema->resultset('Employee')->search(undef, {
1401     select => [
1402       'name',
1403       { count => 'employeeid' }
1404     ],
1405     as => ['name', 'employee_count'],
1406   });
1407
1408   my $employee = $rs->first(); # get the first Employee
1409
1410 If the object against which the search is performed already has an accessor
1411 matching a column name specified in C<as>, the value can be retrieved using
1412 the accessor as normal:
1413
1414   my $name = $employee->name();
1415
1416 If on the other hand an accessor does not exist in the object, you need to
1417 use C<get_column> instead:
1418
1419   my $employee_count = $employee->get_column('employee_count');
1420
1421 You can create your own accessors if required - see
1422 L<DBIx::Class::Manual::Cookbook> for details.
1423
1424 =head2 join
1425
1426 =over 4
1427
1428 =item Value: ($rel_name | \@rel_names | \%rel_names)
1429
1430 =back
1431
1432 Contains a list of relationships that should be joined for this query.  For
1433 example:
1434
1435   # Get CDs by Nine Inch Nails
1436   my $rs = $schema->resultset('CD')->search(
1437     { 'artist.name' => 'Nine Inch Nails' },
1438     { join => 'artist' }
1439   );
1440
1441 Can also contain a hash reference to refer to the other relation's relations.
1442 For example:
1443
1444   package MyApp::Schema::Track;
1445   use base qw/DBIx::Class/;
1446   __PACKAGE__->table('track');
1447   __PACKAGE__->add_columns(qw/trackid cd position title/);
1448   __PACKAGE__->set_primary_key('trackid');
1449   __PACKAGE__->belongs_to(cd => 'MyApp::Schema::CD');
1450   1;
1451
1452   # In your application
1453   my $rs = $schema->resultset('Artist')->search(
1454     { 'track.title' => 'Teardrop' },
1455     {
1456       join     => { cd => 'track' },
1457       order_by => 'artist.name',
1458     }
1459   );
1460
1461 If the same join is supplied twice, it will be aliased to <rel>_2 (and
1462 similarly for a third time). For e.g.
1463
1464   my $rs = $schema->resultset('Artist')->search({
1465     'cds.title'   => 'Down to Earth',
1466     'cds_2.title' => 'Popular',
1467   }, {
1468     join => [ qw/cds cds/ ],
1469   });
1470
1471 will return a set of all artists that have both a cd with title 'Down
1472 to Earth' and a cd with title 'Popular'.
1473
1474 If you want to fetch related objects from other tables as well, see C<prefetch>
1475 below.
1476
1477 =head2 prefetch
1478
1479 =over 4
1480
1481 =item Value: ($rel_name | \@rel_names | \%rel_names)
1482
1483 =back
1484
1485 Contains one or more relationships that should be fetched along with the main
1486 query (when they are accessed afterwards they will have already been
1487 "prefetched").  This is useful for when you know you will need the related
1488 objects, because it saves at least one query:
1489
1490   my $rs = $schema->resultset('Tag')->search(
1491     undef,
1492     {
1493       prefetch => {
1494         cd => 'artist'
1495       }
1496     }
1497   );
1498
1499 The initial search results in SQL like the following:
1500
1501   SELECT tag.*, cd.*, artist.* FROM tag
1502   JOIN cd ON tag.cd = cd.cdid
1503   JOIN artist ON cd.artist = artist.artistid
1504
1505 L<DBIx::Class> has no need to go back to the database when we access the
1506 C<cd> or C<artist> relationships, which saves us two SQL statements in this
1507 case.
1508
1509 Simple prefetches will be joined automatically, so there is no need
1510 for a C<join> attribute in the above search. If you're prefetching to
1511 depth (e.g. { cd => { artist => 'label' } or similar), you'll need to
1512 specify the join as well.
1513
1514 C<prefetch> can be used with the following relationship types: C<belongs_to>,
1515 C<has_one> (or if you're using C<add_relationship>, any relationship declared
1516 with an accessor type of 'single' or 'filter').
1517
1518 =head2 from
1519
1520 =over 4
1521
1522 =item Value: \@from_clause
1523
1524 =back
1525
1526 The C<from> attribute gives you manual control over the C<FROM> clause of SQL
1527 statements generated by L<DBIx::Class>, allowing you to express custom C<JOIN>
1528 clauses.
1529
1530 NOTE: Use this on your own risk.  This allows you to shoot off your foot!
1531 C<join> will usually do what you need and it is strongly recommended that you
1532 avoid using C<from> unless you cannot achieve the desired result using C<join>.
1533
1534 In simple terms, C<from> works as follows:
1535
1536     [
1537         { <alias> => <table>, -join_type => 'inner|left|right' }
1538         [] # nested JOIN (optional)
1539         { <table.column> => <foreign_table.foreign_key> }
1540     ]
1541
1542     JOIN
1543         <alias> <table>
1544         [JOIN ...]
1545     ON <table.column> = <foreign_table.foreign_key>
1546
1547 An easy way to follow the examples below is to remember the following:
1548
1549     Anything inside "[]" is a JOIN
1550     Anything inside "{}" is a condition for the enclosing JOIN
1551
1552 The following examples utilize a "person" table in a family tree application.
1553 In order to express parent->child relationships, this table is self-joined:
1554
1555     # Person->belongs_to('father' => 'Person');
1556     # Person->belongs_to('mother' => 'Person');
1557
1558 C<from> can be used to nest joins. Here we return all children with a father,
1559 then search against all mothers of those children:
1560
1561   $rs = $schema->resultset('Person')->search(
1562       undef,
1563       {
1564           alias => 'mother', # alias columns in accordance with "from"
1565           from => [
1566               { mother => 'person' },
1567               [
1568                   [
1569                       { child => 'person' },
1570                       [
1571                           { father => 'person' },
1572                           { 'father.person_id' => 'child.father_id' }
1573                       ]
1574                   ],
1575                   { 'mother.person_id' => 'child.mother_id' }
1576               ],
1577           ]
1578       },
1579   );
1580
1581   # Equivalent SQL:
1582   # SELECT mother.* FROM person mother
1583   # JOIN (
1584   #   person child
1585   #   JOIN person father
1586   #   ON ( father.person_id = child.father_id )
1587   # )
1588   # ON ( mother.person_id = child.mother_id )
1589
1590 The type of any join can be controlled manually. To search against only people
1591 with a father in the person table, we could explicitly use C<INNER JOIN>:
1592
1593     $rs = $schema->resultset('Person')->search(
1594         undef,
1595         {
1596             alias => 'child', # alias columns in accordance with "from"
1597             from => [
1598                 { child => 'person' },
1599                 [
1600                     { father => 'person', -join_type => 'inner' },
1601                     { 'father.id' => 'child.father_id' }
1602                 ],
1603             ]
1604         },
1605     );
1606
1607     # Equivalent SQL:
1608     # SELECT child.* FROM person child
1609     # INNER JOIN person father ON child.father_id = father.id
1610
1611 =head2 page
1612
1613 =over 4
1614
1615 =item Value: $page
1616
1617 =back
1618
1619 Makes the resultset paged and specifies the page to retrieve. Effectively
1620 identical to creating a non-pages resultset and then calling ->page($page)
1621 on it.
1622
1623 =head2 rows
1624
1625 =over 4
1626
1627 =item Value: $rows
1628
1629 =back
1630
1631 Specifes the maximum number of rows for direct retrieval or the number of
1632 rows per page if the page attribute or method is used.
1633
1634 =head2 group_by
1635
1636 =over 4
1637
1638 =item Value: \@columns
1639
1640 =back
1641
1642 A arrayref of columns to group by. Can include columns of joined tables.
1643
1644   group_by => [qw/ column1 column2 ... /]
1645
1646 =head2 having
1647
1648 =over 4
1649
1650 =item Value: $condition
1651
1652 =back
1653
1654 HAVING is a select statement attribute that is applied between GROUP BY and
1655 ORDER BY. It is applied to the after the grouping calculations have been
1656 done. 
1657
1658   having => { 'count(employee)' => { '>=', 100 } }
1659
1660 =head2 distinct
1661
1662 =over 4
1663
1664 =item Value: (0 | 1)
1665
1666 =back
1667
1668 Set to 1 to group by all columns.
1669
1670 =head2 cache
1671
1672 Set to 1 to cache search results. This prevents extra SQL queries if you
1673 revisit rows in your ResultSet:
1674
1675   my $resultset = $schema->resultset('Artist')->search( undef, { cache => 1 } );
1676   
1677   while( my $artist = $resultset->next ) {
1678     ... do stuff ...
1679   }
1680
1681   $rs->first; # without cache, this would issue a query
1682
1683 By default, searches are not cached.
1684
1685 For more examples of using these attributes, see
1686 L<DBIx::Class::Manual::Cookbook>.
1687
1688 =cut
1689
1690 1;