mce has asked for the wisdom of the Perl Monks concerning the following question:

Hi All,

I have a question in follow up of threads 119171 and 103323. I have exactly the same problem as I store my dbi object as an attribute of my object. For example.

sub opendb { my $self=shift; $self->{_dbh}=DBI->connect(...); return $self->{_dbh}; }
Now I want to commit the transactions in a DESTROY
sub DESTROY { my $self=shift; local($@); if ( $self->{_dbh} and $self->{_dbh}->ping ) { warn; ## to debug $self->{_dbh}->commit; $self->{_dbh}->disconnect; } }
The strange thing is that warn is not called in every script script, although I am sure that I call opendb everytime. I think that the _dbh attribute is destroyed before $self itself is destroyed!

This problem is noted before, and a forced $self->{_dbh}->commit is the best solution. But they mention also that this problem will be solved in future versions of perl.

Now, is this already solved? I use 5.005_03 on solaris (which is not installed nor maintained by me).
And is there a good way of debugging OO problems like this? (using some Devel package?)

Thanks for your assistance,
---------------------------
Dr. Mark Ceulemans
Senior Consultant
IT Masters, Belgium

Replies are listed 'Best First'.
Re: DESTROY and DBI
by krisahoch (Deacon) on Aug 27, 2002 at 12:38 UTC

    Mark,

    I think I see the problem. You are not returning the modified $self. You are returning the connection of opendb's instance of $self.

    sub opendb { my $self=shift; $self->{_dbh}=DBI->connect(...); # return $self->{_dbh}; # Should return $self here return $self; }

    Update: RMGir's responce is absolutely correct. Before posting this I should have asked if the self was blessed.

    Kristofer A. Hoch

      No, krisahoch, he can return the database handle and modify %{$self} as a side effect...

      Mark, is $self properly blessed into the package? Otherwise, DESTROY would never be called...

      It could also be the global cleanup order problem mentioned in this thread.

      Have you tried using this class only from a subroutine? Take the part of the code at the top level that uses this class, and wrap it in a sub. Then put

      $|=1; # make sure we see printouts and warns in order print "Before sub\n"; subThatUsesClass(); print "After sub, I _better_ have seen DESTROY happen...\n";
      If it's the random global cleanups causing the problem, having your variable as a lexical in the sub should get it cleaned up properly...
      --
      Mike

      Edit: Put in correct link to tilly's excellent description of the global cleanup problem...

        Hi Mike,
        Yes, of course I bless my object.
        I even tried this, but I think it is the same as during the exit.
        my $o=new MyClass; my $dbh=$o->opendb; warn $o->{_dbh}; warn $dbh; # the same as the previous line undef $o; exit;
        I cannot put it as a lexical in the sub because I have just written an entire framework (+5000 lines), consisting of a tree of packages in OO, and setting the DBI object as an attribute has an advantage of using persistant DB connections.

        This is the actual opendb routine

        sub opendb { my $self=shift; local($@); if ( not $self->{_dbh} or not $self->{_dbh}->ping) { eval{ $self->{_dbh}=$self->{_dbi}->connect() ; } ; if ( $@ ) { $self->Log('!',$@) and die "db gives error" ; } } $self->{_dbh}->{LongReadLen}=100000; # to handle Long column types $self->{_dbh}->{LongTruncOk}=0; # generates an error when the dat +a is too long $self->{_dbh}->{AutoCommit}=0; # Lets run transactional return $self->{_dbh}; }
        And I call this opendb as much as I like, without going to the db everytime. (note $self->{_dbi} is a object from my superclass of DBI). And the link to tilly's one, that was the one I was referring to, but using flyweightwrapper is a bit overshooting, I think.
        ---------------------------
        Dr. Mark Ceulemans
        Senior Consultant
        IT Masters, Belgium