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]