X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FRow.pm;h=4188d1073133bebdd01336a32c253c2caea1bbd5;hb=c6ec79000b160e7491d9ab9d95d6e69c473b0862;hp=0daf5cbd45e3e33337ab98203cdda1b486912b5c;hpb=e705f5290cf384194c31a807c9bb722c7a167dfd;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Row.pm b/lib/DBIx/Class/Row.pm index 0daf5cb..4188d10 100644 --- a/lib/DBIx/Class/Row.pm +++ b/lib/DBIx/Class/Row.pm @@ -6,8 +6,9 @@ use warnings; use base qw/DBIx::Class/; use Scalar::Util 'blessed'; -use List::Util 'first'; -use Try::Tiny; +use DBIx::Class::_Util qw( dbic_internal_try fail_on_internal_call ); +use DBIx::Class::Carp; +use SQL::Abstract qw( is_literal_value is_plain_value ); ### ### Internal method @@ -22,6 +23,8 @@ BEGIN { use namespace::clean; +__PACKAGE__->mk_group_accessors ( simple => [ in_storage => '_in_storage' ] ); + =head1 NAME DBIx::Class::Row - Basic row methods @@ -45,10 +48,10 @@ relationship accessors of L objects. =head1 NOTE All "Row objects" derived from a Schema-attached L -object (such as a typical C<< L->L >> call) are actually Result +object (such as a typical C<< L-> +L >> call) are actually Result instances, based on your application's -L. +L. L implements most of the row-based communication with the underlying storage, but a Result class B. @@ -63,9 +66,9 @@ objects. Refer to L for more info. =head2 new - my $row = My::Class->new(\%attrs); + my $result = My::Class->new(\%attrs); - my $row = $schema->resultset('MySource')->new(\%colsandvalues); + my $result = $schema->resultset('MySource')->new(\%colsandvalues); =over @@ -115,33 +118,33 @@ with NULL as the default, and save yourself a SELECT. =cut ## It needs to store the new objects somewhere, and call insert on that list later when insert is called on this object. We may need an accessor for these so the user can retrieve them, if just doing ->new(). -## This only works because DBIC doesnt yet care to check whether the new_related objects have been passed all their mandatory columns +## This only works because DBIC doesn't yet care to check whether the new_related objects have been passed all their mandatory columns ## When doing the later insert, we need to make sure the PKs are set. ## using _relationship_data in new and funky ways.. ## check Relationship::CascadeActions and Relationship::Accessor for compat ## tests! sub __new_related_find_or_new_helper { - my ($self, $relname, $data) = @_; + my ($self, $rel_name, $values) = @_; my $rsrc = $self->result_source; # create a mock-object so all new/set_column component overrides will run: - my $rel_rs = $rsrc->related_source($relname)->resultset; - my $new_rel_obj = $rel_rs->new_result($data); + my $rel_rs = $rsrc->related_source($rel_name)->resultset; + my $new_rel_obj = $rel_rs->new_result($values); my $proc_data = { $new_rel_obj->get_columns }; - if ($self->__their_pk_needs_us($relname)) { - MULTICREATE_DEBUG and warn "MC $self constructing $relname via new_result"; + if ($self->__their_pk_needs_us($rel_name)) { + MULTICREATE_DEBUG and print STDERR "MC $self constructing $rel_name via new_result\n"; return $new_rel_obj; } - elsif ($rsrc->_pk_depends_on($relname, $proc_data )) { + elsif ($rsrc->_pk_depends_on($rel_name, $proc_data )) { if (! keys %$proc_data) { # there is nothing to search for - blind create - MULTICREATE_DEBUG and warn "MC $self constructing default-insert $relname"; + MULTICREATE_DEBUG and print STDERR "MC $self constructing default-insert $rel_name\n"; } else { - MULTICREATE_DEBUG and warn "MC $self constructing $relname via find_or_new"; + MULTICREATE_DEBUG and print STDERR "MC $self constructing $rel_name via find_or_new\n"; # this is not *really* find or new, as we don't want to double-new the # data (thus potentially double encoding or whatever) my $exists = $rel_rs->find ($proc_data); @@ -152,17 +155,17 @@ sub __new_related_find_or_new_helper { else { my $us = $rsrc->source_name; $self->throw_exception ( - "Unable to determine relationship '$relname' direction from '$us', " - . "possibly due to a missing reverse-relationship on '$relname' to '$us'." + "Unable to determine relationship '$rel_name' direction from '$us', " + . "possibly due to a missing reverse-relationship on '$rel_name' to '$us'." ); } } sub __their_pk_needs_us { # this should maybe be in resultsource. - my ($self, $relname) = @_; - my $source = $self->result_source; - my $reverse = $source->reverse_relationship_info($relname); - my $rel_source = $source->related_source($relname); + my ($self, $rel_name) = @_; + my $rsrc = $self->result_source; + my $reverse = $rsrc->reverse_relationship_info($rel_name); + my $rel_source = $rsrc->related_source($rel_name); my $us = { $self->get_columns }; foreach my $key (keys %$reverse) { # if their primary key depends on us, then we have to @@ -176,31 +179,31 @@ sub new { my ($class, $attrs) = @_; $class = ref $class if ref $class; - my $new = bless { _column_data => {} }, $class; + my $new = bless { _column_data => {}, _in_storage => 0 }, $class; if ($attrs) { $new->throw_exception("attrs must be a hashref") unless ref($attrs) eq 'HASH'; - my $source = delete $attrs->{-result_source}; + my $rsrc = delete $attrs->{-result_source}; if ( my $h = delete $attrs->{-source_handle} ) { - $source ||= $h->resolve; + $rsrc ||= $h->resolve; } - $new->result_source($source) if $source; + $new->result_source_instance($rsrc) if $rsrc; if (my $col_from_rel = delete $attrs->{-cols_from_relations}) { @{$new->{_ignore_at_insert}={}}{@$col_from_rel} = (); } - my ($related,$inflated); + my( $related, $inflated, $colinfos ); foreach my $key (keys %$attrs) { - if (ref $attrs->{$key}) { + if (ref $attrs->{$key} and ! is_literal_value($attrs->{$key}) ) { ## Can we extract this lot to use with update(_or .. ) ? $new->throw_exception("Can't do multi-create without result source") - unless $source; - my $info = $source->relationship_info($key); + unless $rsrc; + my $info = $rsrc->relationship_info($key); my $acc_type = $info->{attrs}{accessor} || ''; if ($acc_type eq 'single') { my $rel_obj = delete $attrs->{$key}; @@ -212,7 +215,7 @@ sub new { $new->{_rel_in_storage}{$key} = 1; $new->set_from_related($key, $rel_obj); } else { - MULTICREATE_DEBUG and warn "MC $new uninserted $key $rel_obj\n"; + MULTICREATE_DEBUG and print STDERR "MC $new uninserted $key $rel_obj\n"; } $related->{$key} = $rel_obj; @@ -232,7 +235,7 @@ sub new { $rel_obj->throw_exception ('A multi relationship can not be pre-existing when doing multicreate. Something went wrong'); } else { MULTICREATE_DEBUG and - warn "MC $new uninserted $key $rel_obj (${\($idx+1)} of $total)\n"; + print STDERR "MC $new uninserted $key $rel_obj (${\($idx+1)} of $total)\n"; } push(@objects, $rel_obj); } @@ -249,18 +252,19 @@ sub new { $new->{_rel_in_storage}{$key} = 1; } else { - MULTICREATE_DEBUG and warn "MC $new uninserted $key $rel_obj"; + MULTICREATE_DEBUG and print STDERR "MC $new uninserted $key $rel_obj\n"; } $inflated->{$key} = $rel_obj; next; - } elsif ($class->has_column($key) - && $class->column_info($key)->{_inflate_info}) { + } + elsif ( + ( $colinfos ||= $rsrc->columns_info ) + ->{$key}{_inflate_info} + ) { $inflated->{$key} = $attrs->{$key}; next; } } - $new->throw_exception("No such column '$key' on $class") - unless $class->has_column($key); $new->store_column($key => $attrs->{$key}); } @@ -276,18 +280,18 @@ sub new { # Each pair does the same thing # (un-inflated, regular column) - my $val = $row->get_column('first_name'); - my $val = $row->first_name; + my $val = $result->get_column('first_name'); + my $val = $result->first_name; - $row->set_column('first_name' => $val); - $row->first_name($val); + $result->set_column('first_name' => $val); + $result->first_name($val); # (inflated column via DBIx::Class::InflateColumn::DateTime) - my $val = $row->get_inflated_column('last_modified'); - my $val = $row->last_modified; + my $val = $result->get_inflated_column('last_modified'); + my $val = $result->last_modified; - $row->set_inflated_column('last_modified' => $val); - $row->last_modified($val); + $result->set_inflated_column('last_modified' => $val); + $result->last_modified($val); =over @@ -309,7 +313,7 @@ is called on the row. =head2 insert - $row->insert; + $result->insert; =over @@ -334,11 +338,11 @@ one, see L for more details. sub insert { my ($self) = @_; return $self if $self->in_storage; - my $source = $self->result_source; + my $rsrc = $self->result_source; $self->throw_exception("No result_source set on this object; can't insert") - unless $source; + unless $rsrc; - my $storage = $source->storage; + my $storage = $rsrc->schema->storage; my $rollback_guard; @@ -348,27 +352,26 @@ sub insert { # insert what needs to be inserted before us my %pre_insert; - for my $relname (keys %related_stuff) { - my $rel_obj = $related_stuff{$relname}; + for my $rel_name (keys %related_stuff) { + my $rel_obj = $related_stuff{$rel_name}; - if (! $self->{_rel_in_storage}{$relname}) { - next unless (blessed $rel_obj && $rel_obj->isa('DBIx::Class::Row')); + if (! $self->{_rel_in_storage}{$rel_name}) { + next unless (blessed $rel_obj && $rel_obj->isa(__PACKAGE__)); - next unless $source->_pk_depends_on( - $relname, { $rel_obj->get_columns } + next unless $rsrc->_pk_depends_on( + $rel_name, { $rel_obj->get_columns } ); # The guard will save us if we blow out of this scope via die $rollback_guard ||= $storage->txn_scope_guard; - MULTICREATE_DEBUG and warn "MC $self pre-reconstructing $relname $rel_obj\n"; + MULTICREATE_DEBUG and print STDERR "MC $self pre-reconstructing $rel_name $rel_obj\n"; my $them = { %{$rel_obj->{_relationship_data} || {} }, $rel_obj->get_columns }; my $existing; # if there are no keys - nothing to search for - if (keys %$them and $existing = $self->result_source - ->related_source($relname) + if (keys %$them and $existing = $rsrc->related_source($rel_name) ->resultset ->find($them) ) { @@ -378,11 +381,11 @@ sub insert { $rel_obj->insert; } - $self->{_rel_in_storage}{$relname} = 1; + $self->{_rel_in_storage}{$rel_name} = 1; } - $self->set_from_related($relname, $rel_obj); - delete $related_stuff{$relname}; + $self->set_from_related($rel_name, $rel_obj); + delete $related_stuff{$rel_name}; } # start a transaction here if not started yet and there is more stuff @@ -393,14 +396,14 @@ sub insert { MULTICREATE_DEBUG and do { no warnings 'uninitialized'; - warn "MC $self inserting (".join(', ', $self->get_columns).")\n"; + print STDERR "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; my $returned_cols = $storage->insert( - $source, + $rsrc, { %current_rowdata }, # what to insert, copy because the storage *will* change it ); @@ -413,7 +416,14 @@ sub insert { or (defined $current_rowdata{$_} xor defined $returned_cols->{$_}) or - (defined $current_rowdata{$_} and $current_rowdata{$_} ne $returned_cols->{$_}) + ( + defined $current_rowdata{$_} + and + # one of the few spots doing forced-stringification + # needed to work around objects with defined stringification + # but *without* overloaded comparison (ugh!) + "$current_rowdata{$_}" ne "$returned_cols->{$_}" + ) ); } @@ -423,29 +433,29 @@ sub insert { $self->{_dirty_columns} = {}; $self->{related_resultsets} = {}; - foreach my $relname (keys %related_stuff) { - next unless $source->has_relationship ($relname); + foreach my $rel_name (keys %related_stuff) { + next unless $rsrc->has_relationship ($rel_name); - my @cands = ref $related_stuff{$relname} eq 'ARRAY' - ? @{$related_stuff{$relname}} - : $related_stuff{$relname} + my @cands = ref $related_stuff{$rel_name} eq 'ARRAY' + ? @{$related_stuff{$rel_name}} + : $related_stuff{$rel_name} ; - if (@cands && blessed $cands[0] && $cands[0]->isa('DBIx::Class::Row') + if (@cands && blessed $cands[0] && $cands[0]->isa(__PACKAGE__) ) { - my $reverse = $source->reverse_relationship_info($relname); + my $reverse = $rsrc->reverse_relationship_info($rel_name); foreach my $obj (@cands) { $obj->set_from_related($_, $self) for keys %$reverse; - if ($self->__their_pk_needs_us($relname)) { - if (exists $self->{_ignore_at_insert}{$relname}) { - MULTICREATE_DEBUG and warn "MC $self skipping post-insert on $relname"; + if ($self->__their_pk_needs_us($rel_name)) { + if (exists $self->{_ignore_at_insert}{$rel_name}) { + MULTICREATE_DEBUG and print STDERR "MC $self skipping post-insert on $rel_name\n"; } else { - MULTICREATE_DEBUG and warn "MC $self inserting $relname $obj"; + MULTICREATE_DEBUG and print STDERR "MC $self inserting $rel_name $obj\n"; $obj->insert; } } else { - MULTICREATE_DEBUG and warn "MC $self post-inserting $obj"; + MULTICREATE_DEBUG and print STDERR "MC $self post-inserting $obj\n"; $obj->insert(); } } @@ -461,8 +471,8 @@ sub insert { =head2 in_storage - $row->in_storage; # Get value - $row->in_storage(1); # Set value + $result->in_storage; # Get value + $result->in_storage(1); # Set value =over @@ -474,23 +484,16 @@ sub insert { Indicates whether the object exists as a row in the database or not. This is set to true when L, -L or L -are used. +L or L +are invoked. Creating a result object using L, or calling L on one, sets it to false. -=cut - -sub in_storage { - my ($self, $val) = @_; - $self->{_in_storage} = $val if @_ > 1; - return $self->{_in_storage} ? 1 : 0; -} =head2 update - $row->update(\%columns?) + $result->update(\%columns?) =over @@ -501,7 +504,7 @@ sub in_storage { =back Throws an exception if the result object is not yet in the database, -according to L. +according to L. Returns the object itself. This method issues an SQL UPDATE query to commit any changes to the object to the database if required (see L). @@ -520,9 +523,9 @@ 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()')->update(); + $result->last_modified(\'NOW()')->update(); # OR - $row->update({ last_modified => \'NOW()' }); + $result->update({ last_modified => \'NOW()' }); The update will pass the values verbatim into SQL. (See L docs). The values in your Result object will NOT change @@ -530,7 +533,7 @@ as a result of the update call, if you want the object to be updated with the actual values from the database, call L after the update. - $row->update()->discard_changes(); + $result->update()->discard_changes(); To determine before calling this method, which column values have changed and will be updated, call L. @@ -552,7 +555,7 @@ sub update { $self->throw_exception( "Not in database" ) unless $self->in_storage; - my $rows = $self->result_source->storage->update( + my $rows = $self->result_source->schema->storage->update( $self->result_source, \%to_update, $self->_storage_ident_condition ); if ($rows == 0) { @@ -568,7 +571,7 @@ sub update { =head2 delete - $row->delete + $result->delete =over @@ -614,27 +617,24 @@ sub delete { if (ref $self) { $self->throw_exception( "Not in database" ) unless $self->in_storage; - $self->result_source->storage->delete( + $self->result_source->schema->storage->delete( $self->result_source, $self->_storage_ident_condition ); delete $self->{_column_data_in_storage}; - $self->in_storage(undef); + $self->in_storage(0); } else { - my $rsrc = try { $self->result_source_instance } - or $self->throw_exception("Can't do class delete without a ResultSource instance"); - - my $attrs = @_ > 1 && ref $_[$#_] eq 'HASH' ? { %{pop(@_)} } : {}; + my $attrs = @_ > 1 && ref $_[-1] eq 'HASH' ? { %{pop(@_)} } : {}; my $query = ref $_[0] eq 'HASH' ? $_[0] : {@_}; - $rsrc->resultset->search(@_)->delete; + $self->result_source->resultset->search_rs(@_)->delete; } return $self; } =head2 get_column - my $val = $row->get_column($col); + my $val = $result->get_column($col); =over @@ -655,7 +655,7 @@ will be deflated and returned. Note that if you used the C or the C