[Dbix-class] Problems with belongs_to/might_have on columns with
null values.
Jason Kohles
email at jasonkohles.com
Fri Sep 19 01:41:50 BST 2008
If you are one of the people who is getting tired of me popping up on
IRC every couple of weeks to try once again to track down (or at least
positively identify) this problem, you are in luck! I've finally
isolated exactly what the problem I'm running into is.
The problem is with defining a belongs_to or might_have relationship
where the column can contain a null. In my case, the example I've
been using from my application is a timezone_id column that refers to
a table of timezones, which can be null because I'm getting data from
multiple sources and at the time that the record is created, I may not
have any idea what time zone the object is in.
package MyApp::DB::Object;
use parent 'DBIx::Class';
__PACKAGE__->table( 'objects' );
__PACKAGE__->add_columns(
id => { data_type => 'serial' },
name => { data_type => 'varchar', size => 64 },
timezone_id => { data_type => 'integer' },
);
__PACKAGE__->set_primary_key( 'id' );
__PACKAGE__->belongs_to( 'timezone', 'MyApp::DB::Timezone',
'timezone_id' );
package MyApp::DB::Timezone;
use parent 'DBIx::Class';
use overload '""' => 'name', fallback => 1;
__PACKAGE__->table( 'timezones' );
__PACKAGE__->add_columns(
id => { data_type => 'serial' },
name => { data_type => 'varchar', size => 64 },
);
__PACKAGE__->has_many( 'objects', 'MyApp::DB::Object', 'timezone_id' );
Now, given a script like this:
#!/usr/local/bin/perl -w
use strict;
use MyApp::DB;
my $schema = MyApp::DB->connect;
my $object = $schema->resultset( 'Object' )->create( { name => 'no
timezone!' } );
print $object->name,"\n";
print $object->timezone_id,"\n";
print $object->timezone,"\n";
What would you expect to happen? Something like this?
no timezone!
Use of uninitialized value in concatenation (.) or string at ...
Use of uninitialized value in concatenation (.) or string at ...
Yeah, me too. What actually happens is quite a bit different though...
no timezone!
Use of uninitialized value in concatenation (.) or string at ...
Africa/Abidjan
I've tracked the problem down to a this commit...
------------------------------------------------------------------------
r3804 | captainL | 2007-10-04 16:54:08 -0400 (Thu, 04 Oct 2007) | 1 line
fixed search_related from object with unset FK behaviour
------------------------------------------------------------------------
Which contains (excluding tests) only a one-line change to
resolve_condition in DBIx::Class::ResultSource, which is the source of
my problems:
--- lib/DBIx/Class/ResultSource.pm (revision 3803)
+++ lib/DBIx/Class/ResultSource.pm (revision 3804)
@@ -790,7 +790,7 @@
$self->throw_exception("Invalid rel cond val ${v}");
if (ref $for) { # Object
#warn "$self $k $for $v";
- $ret{$k} = $for->get_column($v);
+ $ret{$k} = $for->get_column($v) if $for->has_column_loaded($v);
#warn %ret;
} elsif (!defined $for) { # undef, i.e. "no object"
$ret{$k} = undef;
However, what happens in my case is that when related_resultset calls
resolve_condition, the null column gets skipped, so instead of the
related query being built as 'SELECT me.id,me.name FROM timezones me
WHERE id IS NULL' I end up with just 'SELECT me.id,me.name FROM
timezones', and in this particular case, the first result returned
(out of a couple thousand) is for 'Africa/Abidjan'.
The commit that made this change included 4 new tests, which are:
my $undef_artist_cd = $schema->resultset("CD")->new_result({ 'title'
=> 'badgers', 'year' => 2007 });
is($undef_artist_cd->has_column_loaded('artist'), '', 'FK not loaded');
is($undef_artist_cd->search_related('artist')->count, 3, 'open search
on undef FK');
my $def_artist_cd = $schema->resultset("CD")->new_result({ 'title' =>
'badgers', 'year' => 2007, artist => undef });
is($def_artist_cd->has_column_loaded('artist'), 1, 'FK loaded');
is($def_artist_cd->search_related('artist')->count, 0, 'closed search
on null FK');
Reverting the code change only causes the 'open search on undef FK'
test to fail, but I can't quite seem to wrap my head around the fact
that this behavior seems to be the *intended* result of this patch, is
there a use case that I'm not seeing where having a relationship
return the entire contents of the related table is desirable?
--
Jason Kohles, RHCA RHCDS RHCE
email at jasonkohles.com - http://www.jasonkohles.com/
"A witty saying proves nothing." -- Voltaire
More information about the DBIx-Class
mailing list