X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FRow.pm;h=c59f70a319874e489ac6c0fab6e08be8001b57ac;hb=76031e147d6f0d80ab3ec73a12d373962ade1252;hp=239b327f0b59a54e3e7398a58da21eea6d9bac67;hpb=b4e9f590228d1d73d4089c2ec88372e683e17aeb;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Row.pm b/lib/DBIx/Class/Row.pm index 239b327..c59f70a 100644 --- a/lib/DBIx/Class/Row.pm +++ b/lib/DBIx/Class/Row.pm @@ -7,6 +7,7 @@ use base qw/DBIx::Class/; use DBIx::Class::Exception; use Scalar::Util 'blessed'; +use List::Util 'first'; use Try::Tiny; ### @@ -343,41 +344,29 @@ sub insert { warn "MC $self inserting (".join(', ', $self->get_columns).")\n"; }; + # perform the insert - the storage will return everything it is asked to + # (autoinc primary columns and any retrieve_on_insert columns) my %current_rowdata = $self->get_columns; - - # perform the insert - the storage may return some stuff for us right there - # my $returned_cols = $storage->insert( $source, - \%current_rowdata, + { %current_rowdata }, # what to insert, copy because the storage *will* change it ); for (keys %$returned_cols) { - $self->store_column( - $_, - ( $current_rowdata{$_} = $returned_cols->{$_} ) - ); + $self->store_column($_, $returned_cols->{$_}) + # this ensures we fire store_column only once + # (some asshats like overriding it) + if ( + (!exists $current_rowdata{$_}) + or + (defined $current_rowdata{$_} xor defined $returned_cols->{$_}) + or + (defined $current_rowdata{$_} and $current_rowdata{$_} ne $returned_cols->{$_}) + ); } - # see if any of the pcols still need filling (or re-querying in case of scalarrefs) - my @missing_pri = grep - { ! defined $current_rowdata{$_} or ref $current_rowdata{$_} eq 'SCALAR' } - $self->primary_columns - ; - - if (@missing_pri) { - MULTICREATE_DEBUG and warn "MC $self fetching missing PKs ".join(', ', @missing_pri )."\n"; - - $self->throw_exception( "Missing primary key but Storage doesn't support last_insert_id" ) - unless $storage->can('last_insert_id'); - - my @pri_values = $storage->last_insert_id($self->result_source, @missing_pri); - - $self->throw_exception( "Can't get last insert id" ) - unless (@pri_values == @missing_pri); - - $self->store_column($missing_pri[$_] => $pri_values[$_]) for 0 .. $#missing_pri; - } + delete $self->{_column_data_in_storage}; + $self->in_storage(1); $self->{_dirty_columns} = {}; $self->{related_resultsets} = {}; @@ -411,10 +400,8 @@ sub insert { } } - $self->in_storage(1); - delete $self->{_orig_ident}; - delete $self->{_orig_ident_failreason}; delete $self->{_ignore_at_insert}; + $rollback_guard->commit if $rollback_guard; return $self; @@ -481,7 +468,7 @@ to C, e.g. ( { %{ $href } } ) If the values passed or any of the column values set on the object contain scalar references, e.g.: - $row->last_modified(\'NOW()'); + $row->last_modified(\'NOW()')->update(); # OR $row->update({ last_modified => \'NOW()' }); @@ -511,14 +498,10 @@ sub update { my %to_update = $self->get_dirty_columns or return $self; - my $ident_cond = $self->{_orig_ident} || $self->ident_condition; $self->throw_exception( "Not in database" ) unless $self->in_storage; - $self->throw_exception($self->{_orig_ident_failreason}) - if ! keys %$ident_cond; - my $rows = $self->result_source->storage->update( - $self->result_source, \%to_update, $ident_cond + $self->result_source, \%to_update, $self->_storage_ident_condition ); if ($rows == 0) { $self->throw_exception( "Can't update ${self}: row not found" ); @@ -527,7 +510,7 @@ sub update { } $self->{_dirty_columns} = {}; $self->{related_resultsets} = {}; - delete $self->{_orig_ident}; + delete $self->{_column_data_in_storage}; return $self; } @@ -579,15 +562,11 @@ sub delete { if (ref $self) { $self->throw_exception( "Not in database" ) unless $self->in_storage; - my $ident_cond = $self->{_orig_ident} || $self->ident_condition; - $self->throw_exception($self->{_orig_ident_failreason}) - if ! keys %$ident_cond; - $self->result_source->storage->delete( - $self->result_source, $ident_cond + $self->result_source, $self->_storage_ident_condition ); - delete $self->{_orig_ident}; # no longer identifiable + delete $self->{_column_data_in_storage}; $self->in_storage(undef); } else { @@ -614,7 +593,7 @@ sub delete { =back Throws an exception if the column name given doesn't exist according -to L. +to L. Returns a raw column value from the row object, if it has already been fetched from the database or set by an accessor. @@ -852,25 +831,16 @@ instead, see L. sub set_column { my ($self, $column, $new_value) = @_; - # if we can't get an ident condition on first try - mark the object as unidentifiable - # (by using an empty hashref) and store the error for further diag - unless ($self->{_orig_ident}) { - try { - $self->{_orig_ident} = $self->ident_condition - } - catch { - $self->{_orig_ident_failreason} = $_; - $self->{_orig_ident} = {}; - }; - } + my $had_value = $self->has_column_loaded($column); + my ($old_value, $in_storage) = ($self->get_column($column), $self->in_storage) + if $had_value; - my $old_value = $self->get_column($column); $new_value = $self->store_column($column, $new_value); my $dirty = $self->{_dirty_columns}{$column} || - $self->in_storage # no point tracking dirtyness on uninserted data + $in_storage # no point tracking dirtyness on uninserted data ? ! $self->_eq_column_values ($column, $old_value, $new_value) : 1 ; @@ -899,6 +869,21 @@ sub set_column { delete $self->{_inflated_column}{$rel}; } } + + if ( + # value change from something (even if NULL) + $had_value + and + # no storage - no storage-value + $in_storage + and + # no value already stored (multiple changes before commit to storage) + ! exists $self->{_column_data_in_storage}{$column} + and + $self->_track_storage_value($column) + ) { + $self->{_column_data_in_storage}{$column} = $old_value; + } } return $new_value; @@ -924,6 +909,13 @@ sub _eq_column_values { } } +# returns a boolean indicating if the passed column should have its original +# value tracked between column changes and commitment to storage +sub _track_storage_value { + my ($self, $col) = @_; + return defined first { $col eq $_ } ($self->primary_columns); +} + =head2 set_columns $row->set_columns({ $col => $val, ... }); @@ -1147,41 +1139,33 @@ sub inflate_result { foreach my $pre (keys %{$prefetch||{}}) { - my $pre_source = $source->related_source($pre) - or $class->throw_exception("Can't prefetch non-existent relationship ${pre}"); - - my $accessor = $source->relationship_info($pre)->{attrs}{accessor} - or $class->throw_exception("No accessor for prefetched $pre"); - my @pre_vals; - if (ref $prefetch->{$pre}[0] eq 'ARRAY') { - @pre_vals = @{$prefetch->{$pre}}; + if (! @{$prefetch->{$pre}}) { + # nothing, empty @pre_vals is put in the caches } - elsif ($accessor eq 'multi') { - $class->throw_exception("Implicit prefetch (via select/columns) not supported with accessor 'multi'"); + elsif (ref $prefetch->{$pre}[0] eq 'ARRAY') { + @pre_vals = @{$prefetch->{$pre}}; } else { @pre_vals = $prefetch->{$pre}; } + my $pre_source = $source->related_source($pre); + + my $accessor = $source->relationship_info($pre)->{attrs}{accessor} + or $class->throw_exception("No accessor type declared for prefetched relationship '$pre'"); + my @pre_objects; for my $me_pref (@pre_vals) { - # FIXME - this should not be necessary - # the collapser currently *could* return bogus elements with all - # columns set to undef - my $has_def; - for (values %{$me_pref->[0]}) { - if (defined $_) { - $has_def++; - last; - } - } - next unless $has_def; + # FIXME SUBOPTIMAL - the new row parsers can very well optimize + # this away entirely, and *never* return such empty rows. + # For now we maintain inflate_result API backcompat + next unless first { defined $_ } values %{$me_pref->[0]}; - push @pre_objects, $pre_source->result_class->inflate_result( - $pre_source, @$me_pref - ); + push @pre_objects, $pre_source->result_class->inflate_result( + $pre_source, @$me_pref + ); } if ($accessor eq 'single') { @@ -1380,12 +1364,7 @@ sub get_from_storage { $resultset = $resultset->search(undef, $attrs); } - my $ident_cond = $self->{_orig_ident} || $self->ident_condition; - - $self->throw_exception($self->{_orig_ident_failreason}) - if ! keys %$ident_cond; - - return $resultset->find($ident_cond); + return $resultset->find($self->_storage_ident_condition); } =head2 discard_changes ($attrs?) @@ -1445,7 +1424,6 @@ sub discard_changes { } } - =head2 throw_exception See L. @@ -1478,8 +1456,6 @@ sub throw_exception { Returns the primary key(s) for a row. Can't be called as a class method. Actually implemented in L -1; - =head1 AUTHORS Matt S. Trout