Hi there,


We're currently upgrading our DBIC from v0.08012 to the latest version 
(v0.08120) on a project I'm working on.  Most things are going smoothly, but 
we've run into a use case for find_or_create_related that used to work in the 
older DBIC but no longer does.  We had some code that was calling 
$row->find_or_create_related('rel', { ... }) on a new row object that hasn't 
been inserted into the database yet.  This ought to work because the 
relationship is a belongs_to, so it doesn't need to know anything about $row.  
In the new version, the related row is always created regardless of whether the 
related object already exists in the DB.



We can change the code easily to just call find_or_create() on the related 
table itself, but we were doing it this way because we wanted to find_or_create 
on the related table without having to know what class to search on (that's my 
guess, anyway - the original impetus for the way this was written is long gone 
now).  Having the new row object and the relationship names was an easy way to 
do this, and seems like a quirky yet valid use case, so I would argue that it 
ought to be supported.  Or at least it would be nice if there was some kind of 
error or warning or something, rather than the current behavior which is to 
silently create a new row in the related table even if the find() would have 
succeeded.



I'll provide more detail about my exact use case below, and I'm happy to 
provide anything else.









We have two related tables, build_data and build_urls, which are pasted below 
with the irrelevant columns and relationships removed:



=======================

=== BEGIN BuildData ===

=======================



package NBT::Schema::Icebox::BuildData;



# Created by DBIx::Class::Schema::Loader # DO NOT MODIFY THE FIRST PART OF THIS 
FILE



use strict;

use warnings;



use base 'DBIx::Class::Core';





=head1 NAME



NBT::Schema::Icebox::BuildData



=cut



__PACKAGE__->table("build_data");



=head1 ACCESSORS



=head2 id



  data_type: INT

  default_value: undef

  extra: HASH(0xa042260)

  is_auto_increment: 1

  is_nullable: 0

  size: 10



=head2 build_url



  data_type: INT

  default_value: undef

  extra: HASH(0xa047638)

  is_foreign_key: 1

  is_nullable: 1

  size: 10



=cut



__PACKAGE__->add_columns(

  "id",

  {

    data_type => "INT",

    default_value => undef,

    extra => { unsigned => 1 },

    is_auto_increment => 1,

    is_nullable => 0,

    size => 10,

  },

  "build_url",

  {

    data_type => "INT",

    default_value => undef,

    extra => { unsigned => 1 },

    is_foreign_key => 1,

    is_nullable => 1,

    size => 10,

  },

);

__PACKAGE__->set_primary_key("id");

__PACKAGE__->add_unique_constraint("build_pxe_branch", ["build_url", 
"pxe_kernel", "branch"]);



=head1 RELATIONS



=head2 build_url



Type: belongs_to



Related object: L<NBT::Schema::Icebox::BuildUrls>



=cut



__PACKAGE__->belongs_to(

  "build_url",

  "NBT::Schema::Icebox::BuildUrls",

  { id => "build_url" },

  { join_type => "LEFT" },

);









=======================

=== BEGIN BuildUrls ===

=======================





package NBT::Schema::Icebox::BuildUrls;



# Created by DBIx::Class::Schema::Loader # DO NOT MODIFY THE FIRST PART OF THIS 
FILE



use strict;

use warnings;



use base 'DBIx::Class::Core';





=head1 NAME



NBT::Schema::Icebox::BuildUrls



=cut



__PACKAGE__->table("build_urls");



=head1 ACCESSORS



=head2 id



  data_type: INT

  default_value: undef

  extra: HASH(0xa04789c)

  is_auto_increment: 1

  is_nullable: 0

  size: 10



=head2 name



  data_type: VARCHAR

  default_value: undef

  is_nullable: 0

  size: 255



=cut



__PACKAGE__->add_columns(

  "id",

  {

    data_type => "INT",

    default_value => undef,

    extra => { unsigned => 1 },

    is_auto_increment => 1,

    is_nullable => 0,

    size => 10,

  },

  "name",

  {

    data_type => "VARCHAR",

    default_value => undef,

    is_nullable => 0,

    size => 255,

  },

);

__PACKAGE__->set_primary_key("id");



=head1 RELATIONS



=head2 build_datas



Type: has_many



Related object: L<NBT::Schema::Icebox::BuildData>



=cut



__PACKAGE__->has_many(

  "build_datas",

  "NBT::Schema::Icebox::BuildData",

  { "foreign.build_url" => "self.id" },

);





# Created by DBIx::Class::Schema::Loader v0.05003 @ 2010-03-17 16:38:08 # DO 
NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Ln4fU7T0Koi6pqWzS6APfw





===================

=== END PASTIES ===

===================







We have some code that does a find_or_create_related call like this:



    $build_datum = 
$schema->resultset("NBT::Schema::Icebox::BuildData")->new_result({});

    # actually we loop through a list of relationships next, but I'm keeping it 
simple for the sake of the example

    $build_datum->find_or_create_related("build_url", { name => '...' });



This produces some strange SQL that looks like this:



    SELECT me.id, me.name, me.modified_ts FROM build_urls me WHERE ( ( me.name 
= ? AND 1 = 0 ) ): '...'



The 'AND 1 = 0' condition makes the select impossible, so a related row is 
always created.  The impossible condition seems to come from 
DBIx::Class::ResultSource->_resolve_condition(), which decides that the 
relationship condition is unresolvable and returns \'1 = 0'.  The condition is 
'unresolvable' because the build_url column is not in_storage on $build_datum, 
because $build_datum is a new_result.  If the 'AND 1 = 0' were left out, this 
would do the right thing, I think.  But maybe theres a good reason for its 
existence in this case and I'm just confused?



Thanks for your time,



Byron Young (original author, I'm sending this for him since he can't mail the 
list for some reason -Robert)

_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/[email protected]

Reply via email to