[CDBI] Subclass load order problem with relationships

lianna at planet.nl lianna at planet.nl
Thu Oct 12 16:37:20 BST 2006


Hi,

This problem has been biting me for days until I eventually found the cause
(class loading order) and a way to reproduce it. I am not sure however
whether
this is a CDBI bug, and if there is a way to solve it other than to make
sure
my classes aren't preloaded prematurely. I'm using CDBI 3.0.15.

I suspect it may be related to this bug, but I'm not entirely sure about
that
so that's why I'm posting here:
http://rt.cpan.org/Public/Bug/Display.html?id=14878


In my particular application I'm working on a CMDB where all devices are
stored
in one common table since they have a lot of common information. Some of
these
devices however also require specific additional information (a router
is not
quite like a server), so I'm utilizing subclasses and additional tables,
and I'm
bless()'ing into the subclass at loadtime if necessary. This worked well
until I
moved my classes into separate files and I had a lot less control over class
loading order. What I noticed foremost was that my date columns
(inflated/deflated
to Time::Piece objects) stopped working for my subclasses. They became
simple
scalar columns. It took me a long time to work out that it was caused by my
subclasses being loaded first.


Output of the programs below:

$ ./testgood.pl
My::TestCDBI: type of dateadded is Time::Piece
My::TestCDBI::Child: type of dateadded is Time::Piece
My::TestCDBI::ChildHasMany: type of dateadded is Time::Piece
$ ./testbad.pl   
My::TestCDBI: type of dateadded is Time::Piece
My::TestCDBI::Child: type of dateadded is Time::Piece
My::TestCDBI::ChildHasMany: type of dateadded is __scalar__

Working program, testgood.pl:

###############################################################################
#!/usr/bin/perl

package My::DBI;
use base 'Class::DBI';

__PACKAGE__->connection( 'dbi:SQLite:test.db', '', '' );

package My::TestCDBI;
use base 'My::DBI';

__PACKAGE__->table( 'testtable' );
__PACKAGE__->columns( Primary => qw/id/ );
__PACKAGE__->columns( Essential => qw/dateadded/ );

__PACKAGE__->has_a( 'dateadded' => 'Time::Piece',
   inflate => sub { return undef if not defined $_[0];
Time::Piece->strptime( shift, '%Y-%m-%d %T' ) },
   deflate => 'datetime',
);

package My::TestCDBI::Child;
use base 'My::TestCDBI';

package My::TestCDBI::ChildHasMany;
use base 'My::TestCDBI';

__PACKAGE__->has_many( 'dummies' => 'My::TestCDBI::Dummy' => 'id' );

package My::TestCDBI::Dummy;
use base 'My::TestCDBI';

__PACKAGE__->table( 'dummy' );
__PACKAGE__->columns( Primary => qw/id/ );
__PACKAGE__->columns( Essential => qw/dummy/ );


package main;

my $dbh = My::DBI->db_Main;
$dbh->do(qq{ DROP TABLE IF EXISTS testtable });
$dbh->do(qq{
   CREATE TABLE testtable (
     ID integer primary key,
     DateAdded datetime
   )
});

foreach ( qw( My::TestCDBI My::TestCDBI::Child
My::TestCDBI::ChildHasMany ) ) {
   my $obj = $_->insert({
      'dateadded' => '2001-02-03 04:56:00'
   });
   print $_.": type of dateadded is ".(ref( $obj->dateadded ) ||
'__scalar__')."\n";
}

###############################################################################


Broken program, testbad.pl:

###############################################################################
#!/usr/bin/perl

package My::DBI;
use base 'Class::DBI';

__PACKAGE__->connection( 'dbi:SQLite:test.db', '', '' );

package My::TestCDBI::Child;
use base 'My::TestCDBI';

package My::TestCDBI::ChildHasMany;
use base 'My::TestCDBI';

__PACKAGE__->has_many( 'dummies' => 'My::TestCDBI::Dummy' => 'id' );

package My::TestCDBI;
use base 'My::DBI';

__PACKAGE__->table( 'testtable' );
__PACKAGE__->columns( Primary => qw/id/ );
__PACKAGE__->columns( Essential => qw/dateadded/ );

__PACKAGE__->has_a( 'dateadded' => 'Time::Piece',
   inflate => sub { return undef if not defined $_[0];
Time::Piece->strptime( shift, '%Y-%m-%d %T' ) },
   deflate => 'datetime',
);

package My::TestCDBI::Dummy;
use base 'My::TestCDBI';

__PACKAGE__->table( 'dummy' );
__PACKAGE__->columns( Primary => qw/id/ );
__PACKAGE__->columns( Essential => qw/dummy/ );


package main;

my $dbh = My::DBI->db_Main;
$dbh->do(qq{ DROP TABLE IF EXISTS testtable });
$dbh->do(qq{
   CREATE TABLE testtable (
     ID integer primary key,
     DateAdded datetime
   )
});

foreach ( qw( My::TestCDBI My::TestCDBI::Child
My::TestCDBI::ChildHasMany ) ) {
   my $obj = $_->insert({
      'dateadded' => '2001-02-03 04:56:00'
   });
   print $_.": type of dateadded is ".(ref( $obj->dateadded ) ||
'__scalar__')."\n";
}

###############################################################################

Note that the only difference between these two programs is the location
of the
declaration of the My::TestCDBI::Child* classes - in testbad.pl they
come before
the declaration of My::TestCDBI, in testgood.pl they come after that.

As far as I've been able to establish so far any kind of relationship
defined in
the subclass when it's loaded first mess up the relationships that are
defined in
the parent class. If there are no such definitions there is no problem,
as the
My::TestCDBI::Child class shows - the datetime column continues to work
there.

Does someone have a solution to this? Or maybe it should be filed as a bug?

Cheers,
Lianna




More information about the ClassDBI mailing list