X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSource.pm;h=d7bcd7238653ca434cfdf097331462778143d1ab;hb=a4812caaab21149be7c3c65ad6c0388046430130;hp=a8c2e85239ae1b80c1bc09d2540f7d04c868ee2f;hpb=1e0daa970a7d91ad1e9d6fd3c80a85760ca69327;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index a8c2e85..d7bcd72 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -7,11 +7,10 @@ use DBIx::Class::ResultSet; use DBIx::Class::ResultSourceHandle; use DBIx::Class::Exception; -use Carp::Clan qw/^DBIx::Class/; +use DBIx::Class::Carp; use Try::Tiny; use List::Util 'first'; -use Scalar::Util qw/weaken isweak/; -use Storable qw/nfreeze thaw/; +use Scalar::Util qw/blessed weaken isweak/; use namespace::clean; use base qw/DBIx::Class/; @@ -32,18 +31,18 @@ DBIx::Class::ResultSource - Result source object # Create a table based result source, in a result class. - package MyDB::Schema::Result::Artist; + package MyApp::Schema::Result::Artist; use base qw/DBIx::Class::Core/; __PACKAGE__->table('artist'); __PACKAGE__->add_columns(qw/ artistid name /); __PACKAGE__->set_primary_key('artistid'); - __PACKAGE__->has_many(cds => 'MyDB::Schema::Result::CD'); + __PACKAGE__->has_many(cds => 'MyApp::Schema::Result::CD'); 1; # Create a query (view) based result source, in a result class - package MyDB::Schema::Result::Year2000CDs; + package MyApp::Schema::Result::Year2000CDs; use base qw/DBIx::Class::Core/; __PACKAGE__->load_components('InflateColumn::DateTime'); @@ -254,8 +253,20 @@ generate a new key value. If not specified, L will attempt to retrieve the name of the sequence from the database automatically. +=item retrieve_on_insert + + { retrieve_on_insert => 1 } + +For every column where this is set to true, DBIC will retrieve the RDBMS-side +value upon a new row insertion (normally only the autoincrement PK is +retrieved on insert). C is used automatically if +supported by the underlying storage, otherwise an extra SELECT statement is +executed to retrieve the missing data. + =item auto_nextval + { auto_nextval => 1 } + Set this to a true value for a column whose value is retrieved automatically from a sequence or function (if supported by your Storage driver.) For a sequence, if you do not use a trigger to get the nextval, you have to set the @@ -428,7 +439,7 @@ sub columns { my $columns_info = $source->columns_info; Like L but returns information for the requested columns. If -the optional column-list arrayref is ommitted it returns info on all columns +the optional column-list arrayref is omitted it returns info on all columns currently defined on the ResultSource via L. =cut @@ -634,7 +645,7 @@ sub sequence { my ($self,$seq) = @_; my @pks = $self->primary_columns - or next; + or return; $_->{sequence} = $seq for values %{ $self->columns_info (\@pks) }; @@ -1487,7 +1498,8 @@ sub _resolve_join { -alias => $as, -relation_chain_depth => $seen->{-relation_chain_depth} || 0, }, - $self->_resolve_condition($rel_info->{cond}, $as, $alias) ]; + $self->_resolve_condition($rel_info->{cond}, $as, $alias, $join) + ]; } } @@ -1539,14 +1551,89 @@ sub resolve_condition { $self->_resolve_condition (@_); } -# Resolves the passed condition to a concrete query fragment. If given an alias, -# returns a join condition; if given an object, inverts that object to produce -# a related conditional from that object. -our $UNRESOLVABLE_CONDITION = \'1 = 0'; +our $UNRESOLVABLE_CONDITION = \ '1 = 0'; +# Resolves the passed condition to a concrete query fragment and a flag +# indicating whether this is a cross-table condition. Also an optional +# list of non-triviail values (notmally conditions) returned as a part +# of a joinfree condition hash sub _resolve_condition { - my ($self, $cond, $as, $for) = @_; - if (ref $cond eq 'HASH') { + my ($self, $cond, $as, $for, $relname) = @_; + + my $obj_rel = !!blessed $for; + + if (ref $cond eq 'CODE') { + my $relalias = $obj_rel ? 'me' : $as; + + my ($crosstable_cond, $joinfree_cond) = $cond->({ + self_alias => $obj_rel ? $as : $for, + foreign_alias => $relalias, + self_resultsource => $self, + foreign_relname => $relname || ($obj_rel ? $as : $for), + self_rowobj => $obj_rel ? $for : undef + }); + + my $cond_cols; + if ($joinfree_cond) { + + # FIXME sanity check until things stabilize, remove at some point + $self->throw_exception ( + "A join-free condition returned for relationship '$relname' whithout a row-object to chain from" + ) unless $obj_rel; + + # FIXME another sanity check + if ( + ref $joinfree_cond ne 'HASH' + or + first { $_ !~ /^\Q$relalias.\E.+/ } keys %$joinfree_cond + ) { + $self->throw_exception ( + "The join-free condition returned for relationship '$relname' must be a hash " + .'reference with all keys being valid columns on the related result source' + ); + } + + # normalize + for (values %$joinfree_cond) { + $_ = $_->{'='} if ( + ref $_ eq 'HASH' + and + keys %$_ == 1 + and + exists $_->{'='} + ); + } + + # see which parts of the joinfree cond are conditionals + my $relcol_list = { map { $_ => 1 } $self->related_source($relname)->columns }; + + for my $c (keys %$joinfree_cond) { + my ($colname) = $c =~ /^ (?: \Q$relalias.\E )? (.+)/x; + + unless ($relcol_list->{$colname}) { + push @$cond_cols, $colname; + next; + } + + if ( + ref $joinfree_cond->{$c} + and + ref $joinfree_cond->{$c} ne 'SCALAR' + and + ref $joinfree_cond->{$c} ne 'REF' + ) { + push @$cond_cols, $colname; + next; + } + } + + return wantarray ? ($joinfree_cond, 0, $cond_cols) : $joinfree_cond; + } + else { + return wantarray ? ($crosstable_cond, 1) : $crosstable_cond; + } + } + elsif (ref $cond eq 'HASH') { my %ret; foreach my $k (keys %{$cond}) { my $v = $cond->{$k}; @@ -1583,18 +1670,29 @@ sub _resolve_condition { } elsif (!defined $as) { # undef, i.e. "no reverse object" $ret{$v} = undef; } else { - $ret{"${as}.${k}"} = "${for}.${v}"; + $ret{"${as}.${k}"} = { -ident => "${for}.${v}" }; } } - return \%ret; - } elsif (ref $cond eq 'ARRAY') { - return [ map { $self->_resolve_condition($_, $as, $for) } @$cond ]; - } else { - die("Can't handle condition $cond yet :("); + + return wantarray + ? ( \%ret, ($obj_rel || !defined $as || ref $as) ? 0 : 1 ) + : \%ret + ; + } + elsif (ref $cond eq 'ARRAY') { + my (@ret, $crosstable); + for (@$cond) { + my ($cond, $crosstab) = $self->_resolve_condition($_, $as, $for, $relname); + push @ret, $cond; + $crosstable ||= $crosstab; + } + return wantarray ? (\@ret, $crosstable) : \@ret; + } + else { + $self->throw_exception ("Can't handle condition $cond for relationship '$relname' yet :("); } } - # Accepts one or more relationships for the current source and returns an # array of column names for each of those relationships. Column names are # prefixed relative to the current source, in accordance with where they appear @@ -1647,6 +1745,7 @@ sub _resolve_prefetch { "Can't prefetch has_many ${pre} (join cond too complex)") unless ref($rel_info->{cond}) eq 'HASH'; my $dots = @{[$as_prefix =~ m/\./g]} + 1; # +1 to match the ".${as_prefix}" + if (my ($fail) = grep { @{[$_ =~ m/\./g]} == $dots } keys %{$collapse}) { my ($last) = ($fail =~ /([^\.]+)$/); @@ -1660,6 +1759,7 @@ sub _resolve_prefetch { . 'Use at your own risk.' ); } + #my @col = map { (/^self\.(.+)$/ ? ("${as_prefix}.$1") : ()); } # values %{$rel_info->{cond}}; $collapse->{".${as_prefix}${pre}"} = [ $rel_source->_pri_cols ]; @@ -1837,11 +1937,11 @@ sub handle { } } -sub STORABLE_freeze { nfreeze($_[0]->handle) } +sub STORABLE_freeze { Storable::nfreeze($_[0]->handle) } sub STORABLE_thaw { my ($self, $cloning, $ice) = @_; - %$self = %{ (thaw $ice)->resolve }; + %$self = %{ (Storable::thaw($ice))->resolve }; } =head2 throw_exception