Subject: | Deep recursion on _flesh |
Included in the attached patch is a test case that causes a "deep recursion on _flesh" error when run against Class::DBI v3.0.12. The deep recursion is being caused by a "before_create" trigger which attempts to assign a default value for the "id" field if it doesn't already have one (this is useful for using UUIDs as your primary keys).
The problem is caused by the "unless $this->id" statement. This calls "$this->get('id')", which notices that "$this->_attribute_exists('id')" returns false, and attempts to call "$this->_flesh" to load that column from the database. Of course, calling "$this->_flesh" calls "$this->id" again, causing the deep recursion.
The patch also includes a fix, which disables the "_flesh" method when being called from within a "before_create" and "deflate_for_create" trigger. This is not a particularly elegant way of solving this problem - although it is effective!
diff -urN Class-DBI-v3.0.12.old/lib/Class/DBI.pm Class-DBI-v3.0.12.new/lib/Class/DBI.pm
--- Class-DBI-v3.0.12.old/lib/Class/DBI.pm 2005-11-04 09:22:56.000000000 +0000
+++ Class-DBI-v3.0.12.new/lib/Class/DBI.pm 2005-12-15 16:50:59.000000000 +0000
@@ -532,6 +532,7 @@
my $class = ref $proto || $proto;
my $self = $class->_init($data);
+ $self->{__are_in_insert_method} = 1;
$self->call_trigger('before_create');
$self->call_trigger('deflate_for_create');
@@ -544,6 +545,7 @@
$self->_attrs($col);
}
$self->_insert_row($real);
+ delete $self->{__are_in_insert_method};
my @primary_columns = $class->primary_columns;
$self->_attribute_store(
@@ -818,6 +820,10 @@
sub _flesh {
my ($self, @groups) = @_;
+ if(ref($self) && $self->{__are_in_insert_method}) {
+ $self->call_trigger('select');
+ return 1;
+ }
my @real = grep $_ ne "TEMP", @groups;
if (my @want = grep !$self->_attribute_exists($_),
$self->__grouper->columns_in(@real)) {
diff -urN Class-DBI-v3.0.12.old/t/26-deep-recursion.t Class-DBI-v3.0.12.new/t/26-deep-recursion.t
--- Class-DBI-v3.0.12.old/t/26-deep-recursion.t 1970-01-01 01:00:00.000000000 +0100
+++ Class-DBI-v3.0.12.new/t/26-deep-recursion.t 2005-12-15 16:38:49.000000000 +0000
@@ -0,0 +1,29 @@
+use strict;
+use Test::More;
+
+#----------------------------------------------------------------------
+# Test lazy loading
+#----------------------------------------------------------------------
+
+BEGIN {
+ eval "use DBD::SQLite";
+ plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 4);
+}
+
+INIT {
+ use lib 't/testlib';
+ use Actor;
+}
+
+
+Actor->add_trigger(before_create => sub {
+ my $this = shift;
+ $this->set_id(123) unless $this->id;
+ $this->set_salary(123) unless $this->salary;
+});
+
+ok(my $a1 = Actor->create({ name => "Bob Bobbage" }),
+ "Create an actor");
+ok($a1->id, "has an id");
+is($a1->name, "Bob Bobbage", "actor name");
+is($a1->salary, 123, "salary ok");