attempt to fix weird overload '0+' bug, modify tests to make sure it works
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSource.pm
CommitLineData
9c992ba1 1package DBIx::Class::ResultSource;
2
3use strict;
4use warnings;
5
6use DBIx::Class::ResultSet;
701da8c4 7use Carp::Clan qw/^DBIx::Class/;
9c992ba1 8
6da5894c 9use Storable;
10
9c992ba1 11use base qw/DBIx::Class/;
12__PACKAGE__->load_components(qw/AccessorGroup/);
13
14__PACKAGE__->mk_group_accessors('simple' =>
fc969005 15 qw/_ordered_columns _columns _primaries _unique_constraints name resultset_attributes schema from _relationships/);
16__PACKAGE__->mk_group_accessors('component_class' => qw/resultset_class result_class/);
9c992ba1 17
18=head1 NAME
19
20DBIx::Class::ResultSource - Result source object
21
22=head1 SYNOPSIS
23
24=head1 DESCRIPTION
25
26A ResultSource is a component of a schema from which results can be directly
27retrieved, most usually a table (see L<DBIx::Class::ResultSource::Table>)
28
29=head1 METHODS
30
31=cut
32
33sub new {
34 my ($class, $attrs) = @_;
35 $class = ref $class if ref $class;
36 my $new = bless({ %{$attrs || {}} }, $class);
37 $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
5ac6a044 38 $new->{resultset_attributes} = { %{$new->{resultset_attributes} || {}} };
6da5894c 39 $new->{_ordered_columns} = [ @{$new->{_ordered_columns}||[]}];
40 $new->{_columns} = { %{$new->{_columns}||{}} };
41 $new->{_relationships} = { %{$new->{_relationships}||{}} };
9c992ba1 42 $new->{name} ||= "!!NAME NOT SET!!";
5afa2a15 43 $new->{_columns_info_loaded} ||= 0;
9c992ba1 44 return $new;
45}
46
988bf309 47=pod
48
5ac6a044 49=head2 add_columns
50
51 $table->add_columns(qw/col1 col2 col3/);
52
53 $table->add_columns('col1' => \%col1_info, 'col2' => \%col2_info, ...);
54
55Adds columns to the result source. If supplied key => hashref pairs uses
56the hashref as the column_info for that column.
57
988bf309 58Repeated calls of this method will add more columns, not replace them.
59
60The contents of the column_info are not set in stone, the following
61keys are currently recognised/used by DBIx::Class.
62
63=over 4
64
65=item accessor
66
67Use this to set the name of the accessor for this column. If unset,
68the name of the column will be used.
69
70=item data_type
71
72This contains the column type, it is automatically filled by the
73L<SQL::Translator::Producer::DBIx::Class::File> producer, and the
74L<DBIx::Class::Schema::Loader> module. If you do not enter the
75data_type, DBIx::Class will attempt to retrieve it from the
76database for you, using L<DBI>s column_info method. The values of this
77key are typically upper-cased.
78
79Currently there is no standard set of values for the data_type, use
80whatever your database(s) support.
81
82=item size
83
84The length of your column, if it is a column type that can have a size
85restriction. This is currently not used by DBIx::Class.
86
87=item is_nullable
88
89If the column is allowed to contain NULL values, set a true value
90(typically 1), here. This is currently not used by DBIx::Class.
91
92=item is_auto_increment
93
94Set this to a true value if this is a column that is somehow
95automatically filled. This is currently not used by DBIx::Class.
96
97=item is_foreign_key
98
99Set this to a true value if this column represents a key from a
100foreign table. This is currently not used by DBIx::Class.
101
102=item default_value
103
104Set this to the default value which will be inserted into this column
105by the database. Can contain either values or functions. This is
106currently not used by DBIx::Class.
107
108=item sequence
109
110If your column is using a sequence to create it's values, set the name
111of the sequence here, to allow the values to be retrieved
112automatically by the L<DBIx::Class::PK::Auto> module. PK::Auto will
113attempt to retrieve the sequence name from the database, if this value
114is left unset.
115
116=back
117
5ac6a044 118=head2 add_column
119
120 $table->add_column('col' => \%info?);
121
122Convenience alias to add_columns
123
124=cut
125
9c992ba1 126sub add_columns {
127 my ($self, @cols) = @_;
571dced3 128 $self->_ordered_columns( \@cols )
129 if !$self->_ordered_columns;
20518cb4 130 my @added;
131 my $columns = $self->_columns;
9c992ba1 132 while (my $col = shift @cols) {
53509665 133
30126ac7 134 my $column_info = ref $cols[0] ? shift(@cols) : {};
53509665 135 # If next entry is { ... } use that for the column info, if not
136 # use an empty hashref
137
20518cb4 138 push(@added, $col) unless exists $columns->{$col};
139
140 $columns->{$col} = $column_info;
9c992ba1 141 }
20518cb4 142 push @{ $self->_ordered_columns }, @added;
30126ac7 143 return $self;
9c992ba1 144}
145
146*add_column = \&add_columns;
147
3842b955 148=head2 has_column
149
988bf309 150 if ($obj->has_column($col)) { ... }
151
9c992ba1 152Returns 1 if the source has a column of this name, 0 otherwise.
988bf309 153
154=cut
9c992ba1 155
156sub has_column {
157 my ($self, $column) = @_;
158 return exists $self->_columns->{$column};
159}
160
87c4e602 161=head2 column_info
9c992ba1 162
988bf309 163 my $info = $obj->column_info($col);
164
165Returns the column metadata hashref for a column. See the description
166of add_column for information on the contents of the hashref.
9c992ba1 167
988bf309 168=cut
9c992ba1 169
170sub column_info {
171 my ($self, $column) = @_;
701da8c4 172 $self->throw_exception("No such column $column")
173 unless exists $self->_columns->{$column};
5afa2a15 174 #warn $self->{_columns_info_loaded}, "\n";
175 if ( ! $self->_columns->{$column}->{data_type}
176 && ! $self->{_columns_info_loaded}
a953d8d9 177 && $self->schema && $self->storage() ){
5afa2a15 178 $self->{_columns_info_loaded}++;
a953d8d9 179 my $info;
180############ eval for the case of storage without table
181 eval{
182 $info = $self->storage->columns_info_for ( $self->from() );
183 };
184 if ( ! $@ ){
185 for my $col ( keys %{$self->_columns} ){
186 for my $i ( keys %{$info->{$col}} ){
187 $self->_columns()->{$col}->{$i} = $info->{$col}->{$i};
188 }
189 }
190 }
191 }
9c992ba1 192 return $self->_columns->{$column};
193}
194
195=head2 columns
196
20518cb4 197 my @column_names = $obj->columns;
198
199Returns all column names in the order they were declared to add_columns
87f0da6a 200
201=cut
9c992ba1 202
203sub columns {
701da8c4 204 my $self=shift;
205 $self->throw_exception("columns() is a read-only accessor, did you mean add_columns()?") if (@_ > 1);
206 return @{$self->{_ordered_columns}||[]};
571dced3 207}
208
87c4e602 209=head2 set_primary_key
210
211=head3 Arguments: (@cols)
87f0da6a 212
9c992ba1 213Defines one or more columns as primary key for this source. Should be
214called after C<add_columns>.
87f0da6a 215
216Additionally, defines a unique constraint named C<primary>.
217
988bf309 218The primary key columns are used by L<DBIx::Class::PK::Auto> to
219retrieve automatically created values from the database.
220
87f0da6a 221=cut
9c992ba1 222
223sub set_primary_key {
224 my ($self, @cols) = @_;
225 # check if primary key columns are valid columns
226 for (@cols) {
701da8c4 227 $self->throw_exception("No such column $_ on table ".$self->name)
9c992ba1 228 unless $self->has_column($_);
229 }
230 $self->_primaries(\@cols);
87f0da6a 231
232 $self->add_unique_constraint(primary => \@cols);
9c992ba1 233}
234
87f0da6a 235=head2 primary_columns
236
9c992ba1 237Read-only accessor which returns the list of primary keys.
30126ac7 238
87f0da6a 239=cut
9c992ba1 240
241sub primary_columns {
242 return @{shift->_primaries||[]};
243}
244
87f0da6a 245=head2 add_unique_constraint
246
247Declare a unique constraint on this source. Call once for each unique
988bf309 248constraint. Unique constraints are used when you call C<find> on a
958916aa 249L<DBIx::Class::ResultSet>, only columns in the constraint are searched,
87f0da6a 250
251 # For e.g. UNIQUE (column1, column2)
252 __PACKAGE__->add_unique_constraint(constraint_name => [ qw/column1 column2/ ]);
253
254=cut
255
256sub add_unique_constraint {
257 my ($self, $name, $cols) = @_;
258
259 for (@$cols) {
701da8c4 260 $self->throw_exception("No such column $_ on table ".$self->name)
87f0da6a 261 unless $self->has_column($_);
262 }
263
264 my %unique_constraints = $self->unique_constraints;
265 $unique_constraints{$name} = $cols;
266 $self->_unique_constraints(\%unique_constraints);
267}
268
269=head2 unique_constraints
270
271Read-only accessor which returns the list of unique constraints on this source.
272
273=cut
274
275sub unique_constraints {
276 return %{shift->_unique_constraints||{}};
277}
278
9c992ba1 279=head2 from
280
281Returns an expression of the source to be supplied to storage to specify
282retrieval from this source; in the case of a database the required FROM clause
283contents.
284
285=cut
286
287=head2 storage
288
988bf309 289Returns the storage handle for the current schema.
290
291See also: L<DBIx::Class::Storage>
9c992ba1 292
293=cut
294
295sub storage { shift->schema->storage; }
296
8452e496 297=head2 add_relationship
298
299 $source->add_relationship('relname', 'related_source', $cond, $attrs);
300
301The relation name can be arbitrary, but must be unique for each relationship
302attached to this result source. 'related_source' should be the name with
303which the related result source was registered with the current schema
304(for simple schemas this is usally either Some::Namespace::Foo or just Foo)
305
306The condition needs to be an SQL::Abstract-style representation of the join
988bf309 307between the tables. For example, if you're creating a rel from Author to Book,
308
309 { 'foreign.author_id' => 'self.id' }
310
311will result in the JOIN clause
312
313 author me JOIN book foreign ON foreign.author_id = me.id
314
8452e496 315You can specify as many foreign => self mappings as necessary.
316
988bf309 317Valid attributes are as follows:
318
319=over 4
320
321=item join_type
322
323Explicitly specifies the type of join to use in the relationship. Any
324SQL join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in
325the SQL command immediately before C<JOIN>.
326
327=item proxy
328
329An arrayref containing a list of accessors in the foreign class to
330proxy in the main class. If, for example, you do the following:
331
332 __PACKAGE__->might_have(bar => 'Bar', undef, { proxy => [ qw/margle/] });
333
334Then, assuming Bar has an accessor named margle, you can do:
335
336 my $obj = Foo->find(1);
337 $obj->margle(10); # set margle; Bar object is created if it doesn't exist
338
339=item accessor
340
341Specifies the type of accessor that should be created for the
342relationship. Valid values are C<single> (for when there is only a single
343related object), C<multi> (when there can be many), and C<filter> (for
344when there is a single related object, but you also want the relationship
345accessor to double as a column accessor). For C<multi> accessors, an
346add_to_* method is also created, which calls C<create_related> for the
347relationship.
348
8452e496 349=back
350
351=cut
352
353sub add_relationship {
354 my ($self, $rel, $f_source_name, $cond, $attrs) = @_;
701da8c4 355 $self->throw_exception("Can't create relationship without join condition") unless $cond;
8452e496 356 $attrs ||= {};
87772e46 357
8452e496 358 my %rels = %{ $self->_relationships };
359 $rels{$rel} = { class => $f_source_name,
87772e46 360 source => $f_source_name,
8452e496 361 cond => $cond,
362 attrs => $attrs };
363 $self->_relationships(\%rels);
364
30126ac7 365 return $self;
87772e46 366
953a18ef 367 # XXX disabled. doesn't work properly currently. skip in tests.
368
8452e496 369 my $f_source = $self->schema->source($f_source_name);
370 unless ($f_source) {
371 eval "require $f_source_name;";
372 if ($@) {
373 die $@ unless $@ =~ /Can't locate/;
374 }
375 $f_source = $f_source_name->result_source;
87772e46 376 #my $s_class = ref($self->schema);
377 #$f_source_name =~ m/^${s_class}::(.*)$/;
378 #$self->schema->register_class(($1 || $f_source_name), $f_source_name);
379 #$f_source = $self->schema->source($f_source_name);
8452e496 380 }
381 return unless $f_source; # Can't test rel without f_source
382
383 eval { $self->resolve_join($rel, 'me') };
384
385 if ($@) { # If the resolve failed, back out and re-throw the error
386 delete $rels{$rel}; #
387 $self->_relationships(\%rels);
701da8c4 388 $self->throw_exception("Error creating relationship $rel: $@");
8452e496 389 }
390 1;
391}
392
87c4e602 393=head2 relationships
8452e496 394
395Returns all valid relationship names for this source
396
397=cut
398
399sub relationships {
400 return keys %{shift->_relationships};
401}
402
87c4e602 403=head2 relationship_info
404
405=head3 Arguments: ($relname)
8452e496 406
407Returns the relationship information for the specified relationship name
408
409=cut
410
411sub relationship_info {
412 my ($self, $rel) = @_;
413 return $self->_relationships->{$rel};
414}
415
87c4e602 416=head2 has_relationship
417
418=head3 Arguments: ($rel)
953a18ef 419
420Returns 1 if the source has a relationship of this name, 0 otherwise.
988bf309 421
422=cut
953a18ef 423
424sub has_relationship {
425 my ($self, $rel) = @_;
426 return exists $self->_relationships->{$rel};
427}
428
87c4e602 429=head2 resolve_join
430
431=head3 Arguments: ($relation)
8452e496 432
433Returns the join structure required for the related result source
434
435=cut
436
437sub resolve_join {
489709af 438 my ($self, $join, $alias, $seen) = @_;
439 $seen ||= {};
87772e46 440 if (ref $join eq 'ARRAY') {
489709af 441 return map { $self->resolve_join($_, $alias, $seen) } @$join;
87772e46 442 } elsif (ref $join eq 'HASH') {
489709af 443 return
887ce227 444 map {
445 my $as = ($seen->{$_} ? $_.'_'.($seen->{$_}+1) : $_);
446 ($self->resolve_join($_, $alias, $seen),
447 $self->related_source($_)->resolve_join($join->{$_}, $as, $seen));
448 } keys %$join;
87772e46 449 } elsif (ref $join) {
701da8c4 450 $self->throw_exception("No idea how to resolve join reftype ".ref $join);
87772e46 451 } else {
489709af 452 my $count = ++$seen->{$join};
453 #use Data::Dumper; warn Dumper($seen);
454 my $as = ($count > 1 ? "${join}_${count}" : $join);
3842b955 455 my $rel_info = $self->relationship_info($join);
701da8c4 456 $self->throw_exception("No such relationship ${join}") unless $rel_info;
3842b955 457 my $type = $rel_info->{attrs}{join_type} || '';
489709af 458 return [ { $as => $self->related_source($join)->from,
953a18ef 459 -join_type => $type },
489709af 460 $self->resolve_condition($rel_info->{cond}, $as, $alias) ];
953a18ef 461 }
462}
463
87c4e602 464=head2 resolve_condition
465
466=head3 Arguments: ($cond, $as, $alias|$object)
953a18ef 467
3842b955 468Resolves the passed condition to a concrete query fragment. If given an alias,
953a18ef 469returns a join condition; if given an object, inverts that object to produce
470a related conditional from that object.
471
472=cut
473
474sub resolve_condition {
489709af 475 my ($self, $cond, $as, $for) = @_;
953a18ef 476 #warn %$cond;
477 if (ref $cond eq 'HASH') {
478 my %ret;
479 while (my ($k, $v) = each %{$cond}) {
480 # XXX should probably check these are valid columns
701da8c4 481 $k =~ s/^foreign\.// || $self->throw_exception("Invalid rel cond key ${k}");
482 $v =~ s/^self\.// || $self->throw_exception("Invalid rel cond val ${v}");
953a18ef 483 if (ref $for) { # Object
3842b955 484 #warn "$self $k $for $v";
485 $ret{$k} = $for->get_column($v);
486 #warn %ret;
953a18ef 487 } else {
489709af 488 $ret{"${as}.${k}"} = "${for}.${v}";
953a18ef 489 }
953a18ef 490 }
491 return \%ret;
5efe4c79 492 } elsif (ref $cond eq 'ARRAY') {
489709af 493 return [ map { $self->resolve_condition($_, $as, $for) } @$cond ];
953a18ef 494 } else {
495 die("Can't handle this yet :(");
87772e46 496 }
497}
498
87c4e602 499=head2 resolve_prefetch
500
501=head3 Arguments: (hashref/arrayref/scalar)
988bf309 502
b3e8ac9b 503Accepts one or more relationships for the current source and returns an
504array of column names for each of those relationships. Column names are
505prefixed relative to the current source, in accordance with where they appear
506in the supplied relationships. Examples:
507
5ac6a044 508 my $source = $schema->resultset('Tag')->source;
b3e8ac9b 509 @columns = $source->resolve_prefetch( { cd => 'artist' } );
510
511 # @columns =
512 #(
513 # 'cd.cdid',
514 # 'cd.artist',
515 # 'cd.title',
516 # 'cd.year',
517 # 'cd.artist.artistid',
518 # 'cd.artist.name'
519 #)
520
521 @columns = $source->resolve_prefetch( qw[/ cd /] );
522
523 # @columns =
524 #(
525 # 'cd.cdid',
526 # 'cd.artist',
527 # 'cd.title',
528 # 'cd.year'
529 #)
530
531 $source = $schema->resultset('CD')->source;
532 @columns = $source->resolve_prefetch( qw[/ artist producer /] );
533
534 # @columns =
535 #(
536 # 'artist.artistid',
537 # 'artist.name',
538 # 'producer.producerid',
539 # 'producer.name'
540 #)
988bf309 541
b3e8ac9b 542=cut
543
544sub resolve_prefetch {
489709af 545 my ($self, $pre, $alias, $seen) = @_;
546 $seen ||= {};
b3e8ac9b 547 use Data::Dumper;
548 #$alias ||= $self->name;
549 #warn $alias, Dumper $pre;
550 if( ref $pre eq 'ARRAY' ) {
489709af 551 return map { $self->resolve_prefetch( $_, $alias, $seen ) } @$pre;
b3e8ac9b 552 }
553 elsif( ref $pre eq 'HASH' ) {
554 my @ret =
555 map {
489709af 556 $self->resolve_prefetch($_, $alias, $seen),
557 $self->related_source($_)->resolve_prefetch(
558 $pre->{$_}, "${alias}.$_", $seen)
559 } keys %$pre;
b3e8ac9b 560 #die Dumper \@ret;
561 return @ret;
562 }
563 elsif( ref $pre ) {
701da8c4 564 $self->throw_exception( "don't know how to resolve prefetch reftype " . ref $pre);
b3e8ac9b 565 }
566 else {
489709af 567 my $count = ++$seen->{$pre};
568 my $as = ($count > 1 ? "${pre}_${count}" : $pre);
b3e8ac9b 569 my $rel_info = $self->relationship_info( $pre );
701da8c4 570 $self->throw_exception( $self->name . " has no such relationship '$pre'" ) unless $rel_info;
489709af 571 my $as_prefix = ($alias =~ /^.*?\.(.*)$/ ? $1.'.' : '');
572 return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] }
573 $self->related_source($pre)->columns;
b3e8ac9b 574 #warn $alias, Dumper (\@ret);
489709af 575 #return @ret;
b3e8ac9b 576 }
577}
953a18ef 578
87c4e602 579=head2 related_source
580
581=head3 Arguments: ($relname)
87772e46 582
988bf309 583Returns the result source object for the given relationship
87772e46 584
585=cut
586
587sub related_source {
588 my ($self, $rel) = @_;
aea52c85 589 if( !$self->has_relationship( $rel ) ) {
701da8c4 590 $self->throw_exception("No such relationship '$rel'");
aea52c85 591 }
87772e46 592 return $self->schema->source($self->relationship_info($rel)->{source});
8452e496 593}
594
5ac6a044 595=head2 resultset
596
988bf309 597Returns a resultset for the given source, by calling:
5ac6a044 598
988bf309 599 $self->resultset_class->new($self, $self->resultset_attributes)
5ac6a044 600
601=head2 resultset_class
602
988bf309 603Set the class of the resultset, this is useful if you want to create your
604own resultset methods. Create your own class derived from
605L<DBIx::Class::ResultSet>, and set it here.
5ac6a044 606
607=head2 resultset_attributes
608
988bf309 609Specify here any attributes you wish to pass to your specialised resultset.
5ac6a044 610
611=cut
612
613sub resultset {
614 my $self = shift;
615 return $self->resultset_class->new($self, $self->{resultset_attributes});
616}
617
701da8c4 618=head2 throw_exception
619
988bf309 620See throw_exception in L<DBIx::Class::Schema>.
701da8c4 621
622=cut
623
624sub throw_exception {
625 my $self = shift;
626 if (defined $self->schema) {
627 $self->schema->throw_exception(@_);
628 } else {
629 croak(@_);
630 }
631}
632
633
9c992ba1 634=head1 AUTHORS
635
636Matt S. Trout <mst@shadowcatsystems.co.uk>
637
638=head1 LICENSE
639
640You may distribute this code under the same terms as Perl itself.
641
642=cut
643