rollback on disconnect, and disconnect on DESTROY
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI.pm
index 47f88df..c218ef1 100644 (file)
@@ -6,6 +6,7 @@ use DBI;
 use SQL::Abstract::Limit;
 use DBIx::Class::Storage::DBI::Cursor;
 use IO::File;
+use Carp::Clan qw/DBIx::Class/;
 
 BEGIN {
 
@@ -142,11 +143,19 @@ sub limit_dialect {
     return $self->{limit_dialect};
 }
 
+package DBIx::Class::Storage::DBI::DebugCallback;
+
+sub print {
+  my ($self, $string) = @_;
+  $string =~ m/^(\w+)/;
+  ${$self}->($1, $string);
+}
+
 } # End of BEGIN block
 
 use base qw/DBIx::Class/;
 
-__PACKAGE__->load_components(qw/Exception AccessorGroup/);
+__PACKAGE__->load_components(qw/AccessorGroup/);
 
 __PACKAGE__->mk_group_accessors('simple' =>
   qw/connect_info _dbh _sql_maker debug debugfh cursor on_connect_do transaction_depth/);
@@ -157,7 +166,7 @@ sub new {
   $new->transaction_depth(0);
   if (defined($ENV{DBIX_CLASS_STORAGE_DBI_DEBUG}) &&
      ($ENV{DBIX_CLASS_STORAGE_DBI_DEBUG} =~ /=(.+)$/)) {
-    $new->debugfh(IO::File->new($1, 'w')||die "Cannot open trace file $1");
+    $new->debugfh(IO::File->new($1, 'w')||croak "Cannot open trace file $1");
   } else {
     $new->debugfh(IO::File->new('>&STDERR'));
   }
@@ -195,14 +204,50 @@ should be an IO::Handle compatible object (only the C<print> method is
 used).  Initially set to be STDERR - although see information on the
 L<DBIX_CLASS_STORAGE_DBI_DEBUG> environment variable.
 
+=head2 debugcb
+
+Sets a callback to be executed each time a statement is run; takes a sub
+reference. Overrides debugfh. Callback is executed as $sub->($op, $info)
+where $op is SELECT/INSERT/UPDATE/DELETE and $info is what would normally
+be printed.
+
 =cut
 
-sub dbh {
+sub debugcb {
+  my ($self, $cb) = @_;
+  my $cb_obj = bless(\$cb, 'DBIx::Class::Storage::DBI::DebugCallback');
+  $self->debugfh($cb_obj);
+}
+
+sub disconnect {
   my ($self) = @_;
+
+  if( $self->connected ) {
+    $self->_dbh->rollback unless $self->_dbh->{AutoCommit};
+    $self->_dbh->disconnect;
+    $self->_dbh(undef);
+  }
+}
+
+sub connected {
+  my ($self) = @_;
+
   my $dbh;
-  unless (($dbh = $self->_dbh) && $dbh->FETCH('Active') && $dbh->ping) {
+  (($dbh = $self->_dbh) && $dbh->FETCH('Active') && $dbh->ping)
+}
+
+sub ensure_connected {
+  my ($self) = @_;
+
+  unless ($self->connected) {
     $self->_populate_dbh;
   }
+}
+
+sub dbh {
+  my ($self) = @_;
+
+  $self->ensure_connected;
   return $self->_dbh;
 }
 
@@ -277,16 +322,24 @@ sub _execute {
   my ($self, $op, $extra_bind, $ident, @args) = @_;
   my ($sql, @bind) = $self->sql_maker->$op($ident, @args);
   unshift(@bind, @$extra_bind) if $extra_bind;
-  $self->debugfh->print("$sql: @bind\n") if $self->debug;
+  if ($self->debug) {
+      my @debug_bind = map { defined $_ ? $_ : 'NULL' } @bind;
+      $self->debugfh->print("$sql: @debug_bind\n");
+  }
   my $sth = $self->sth($sql,$op);
   @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args
-  my $rv = $sth->execute(@bind);
+  my $rv;
+  if ($sth) {  
+    $rv = $sth->execute(@bind);
+  } else { 
+    croak "'$sql' did not generate a statement.";
+  }
   return (wantarray ? ($rv, $sth, @bind) : $rv);
 }
 
 sub insert {
   my ($self, $ident, $to_insert) = @_;
-  $self->throw( "Couldn't insert ".join(', ', map "$_ => $to_insert->{$_}", keys %$to_insert)." into ${ident}" )
+  croak( "Couldn't insert ".join(', ', map "$_ => $to_insert->{$_}", keys %$to_insert)." into ${ident}" )
     unless ($self->_execute('insert' => [], $ident, $to_insert));
   return $to_insert;
 }
@@ -325,10 +378,14 @@ sub select {
   return $self->cursor->new($self, \@_, $attrs);
 }
 
+# Need to call finish() to work round broken DBDs
+
 sub select_single {
   my $self = shift;
   my ($rv, $sth, @bind) = $self->_select(@_);
-  return $sth->fetchrow_array;
+  my @row = $sth->fetchrow_array;
+  $sth->finish();
+  return @row;
 }
 
 sub sth {
@@ -367,6 +424,8 @@ sub columns_info_for {
     return \%result;
 }
 
+sub DESTROY { shift->disconnect }
+
 1;
 
 =head1 ENVIRONMENT VARIABLES