do not try to silently handle disconnects when in a manual txn_begin/txn_end transaction
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI.pm
index cb23efc..5cc795f 100644 (file)
@@ -3,13 +3,14 @@ package DBIx::Class::Storage::DBI;
 
 use base 'DBIx::Class::Storage';
 
-use strict;
+use strict;    
 use warnings;
 use DBI;
 use SQL::Abstract::Limit;
 use DBIx::Class::Storage::DBI::Cursor;
 use DBIx::Class::Storage::Statistics;
 use IO::File;
+use Scalar::Util 'blessed';
 
 __PACKAGE__->mk_group_accessors(
   'simple' =>
@@ -505,7 +506,9 @@ sub dbh_do {
   ref $coderef eq 'CODE' or $self->throw_exception
     ('$coderef must be a CODE reference');
 
-  return $coderef->($self, $self->_dbh, @_) if $self->{_in_dbh_do};
+  return $coderef->($self, $self->_dbh, @_) if $self->{_in_dbh_do}
+      || $self->{transaction_depth};
+
   local $self->{_in_dbh_do} = 1;
 
   my @result;
@@ -828,6 +831,10 @@ sub _prep_for_execute {
 sub _execute {
   my ($self, $op, $extra_bind, $ident, $bind_attributes, @args) = @_;
   
+  if( blessed($ident) && $ident->isa("DBIx::Class::ResultSource") ) {
+    $ident = $ident->from();
+  }
+  
   my ($sql, @bind) = $self->sql_maker->$op($ident, @args);
   unshift(@bind,
     map { ref $_ eq 'ARRAY' ? $_ : [ '!!dummy', $_ ] } @$extra_bind)
@@ -837,49 +844,55 @@ sub _execute {
         map { defined ($_ && $_->[1]) ? qq{'$_->[1]'} : q{'NULL'} } @bind;
       $self->debugobj->query_start($sql, @debug_bind);
   }
-  my $sth = eval { $self->sth($sql,$op) };
 
-  if (!$sth || $@) {
-    $self->throw_exception(
-      'no sth generated via sql (' . ($@ || $self->_dbh->errstr) . "): $sql"
-    );
-  }
+  my ($rv, $sth);
+  RETRY: while (1) {
+    $sth = eval { $self->sth($sql,$op) };
 
-  my $rv;
-  if ($sth) {
-    my $time = time();
-       
-    $rv = eval {
-       
-      my $placeholder_index = 1; 
+    if (!$sth || $@) {
+      $self->throw_exception(
+        'no sth generated via sql (' . ($@ || $self->_dbh->errstr) . "): $sql"
+      );
+    }
 
-      foreach my $bound (@bind) {
+    if ($sth) {
+      my $time = time();
+      $rv = eval {
+        my $placeholder_index = 1; 
 
-        my $attributes = {};
-        my($column_name, @data) = @$bound;
+        foreach my $bound (@bind) {
 
-        if( $bind_attributes ) {
-          $attributes = $bind_attributes->{$column_name}
-          if defined $bind_attributes->{$column_name};
-        }
+          my $attributes = {};
+          my($column_name, @data) = @$bound;
+
+          if( $bind_attributes ) {
+            $attributes = $bind_attributes->{$column_name}
+            if defined $bind_attributes->{$column_name};
+          }
 
-               foreach my $data (@data)
-               {
-          $data = ref $data ? ''.$data : $data; # stringify args
+          foreach my $data (@data)
+          {
+            $data = ref $data ? ''.$data : $data; # stringify args
 
-          $sth->bind_param($placeholder_index, $data, $attributes);
-          $placeholder_index++;                  
-               }
+            $sth->bind_param($placeholder_index, $data, $attributes);
+            $placeholder_index++;
+          }
+        }
+        $sth->execute();
+      };
+    
+      if ($@ || !$rv) {
+        $self->throw_exception("Error executing '$sql': ".($@ || $sth->errstr))
+          if $self->connected;
+        $self->_populate_dbh;
+      } else {
+        last RETRY;
       }
-      $sth->execute();
-    };
-  
-    if ($@ || !$rv) {
-      $self->throw_exception("Error executing '$sql': ".($@ || $sth->errstr));
+    } else {
+      $self->throw_exception("'$sql' did not generate a statement.");
     }
-  } else {
-    $self->throw_exception("'$sql' did not generate a statement.");
-  }
+  } # While(1) to retry if disconencted
+
   if ($self->debug) {
      my @debug_bind =
        map { defined ($_ && $_->[1]) ? qq{'$_->[1]'} : q{'NULL'} } @bind; 
@@ -898,7 +911,7 @@ sub insert {
     "Couldn't insert ".join(', ',
       map "$_ => $to_insert->{$_}", keys %$to_insert
     )." into ${ident}"
-  ) unless ($self->_execute('insert' => [], $ident, $bind_attributes, $to_insert));
+  ) unless ($self->_execute('insert' => [], $source, $bind_attributes, $to_insert));
   return $to_insert;
 }
 
@@ -929,32 +942,17 @@ sub insert_bulk {
   
   ##use Data::Dumper;
   ##print STDERR Dumper( $data, $sql, [@bind] );
-       
+
   if ($sth) {
   
     my $time = time();
-       
-    #$rv = eval {
-       #
-       #  $sth->execute_array({
-
-       #    ArrayTupleFetch => sub {
-
-       #      my $values = shift @$data;  
-    #      return if !$values; 
-    #      return [ @{$values}[@bind] ];
-       #    },
-         
-       #    ArrayTupleStatus => $tuple_status,
-       #  })
-    #};
-       
-       ## Get the bind_attributes, if any exist
+
+    ## Get the bind_attributes, if any exist
     my $bind_attributes = $self->source_bind_attributes($source);
 
-       ## Bind the values and execute
-       $rv = eval {
-       
+    ## Bind the values and execute
+    $rv = eval {
+
      my $placeholder_index = 1; 
 
         foreach my $bound (@bind) {
@@ -966,20 +964,19 @@ sub insert_bulk {
             $attributes = $bind_attributes->{$column_name}
             if defined $bind_attributes->{$column_name};
           }
-                 
-                 my @data = map { $_->[$data_index] } @$data;
+
+          my @data = map { $_->[$data_index] } @$data;
 
           $sth->bind_param_array( $placeholder_index, [@data], $attributes );
           $placeholder_index++;
       }
-         $sth->execute_array( {ArrayTupleStatus => $tuple_status} );
+      $sth->execute_array( {ArrayTupleStatus => $tuple_status} );
 
-       };
+    };
    
     if ($@ || !defined $rv) {
       my $errors = '';
-      foreach my $tuple (@$tuple_status)
-      {
+      foreach my $tuple (@$tuple_status) {
           $errors .= "\n" . $tuple->[1] if(ref $tuple);
       }
       $self->throw_exception("Error executing '$sql': ".($@ || $errors));
@@ -998,9 +995,8 @@ sub update {
   my $self = shift @_;
   my $source = shift @_;
   my $bind_attributes = $self->source_bind_attributes($source);
-  my $ident = $source->from;
   
-  return $self->_execute('update' => [], $ident, $bind_attributes, @_);
+  return $self->_execute('update' => [], $source, $bind_attributes, @_);
 }
 
 
@@ -1009,9 +1005,8 @@ sub delete {
   my $source = shift @_;
   
   my $bind_attrs = {}; ## If ever it's needed...
-  my $ident = $source->from;
   
-  return $self->_execute('delete' => [], $ident, $bind_attrs, @_);
+  return $self->_execute('delete' => [], $source, $bind_attrs, @_);
 }
 
 sub _select {
@@ -1048,7 +1043,7 @@ sub source_bind_attributes {
   
     my $data_type = $source->column_info($column)->{data_type} || '';
     $bind_attributes->{$column} = $self->bind_attribute_by_data_type($data_type)
-        if $data_type;
+     if $data_type;
   }
 
   return $bind_attributes;
@@ -1138,18 +1133,12 @@ sub _dbh_columns_info_for {
   }
 
   my %result;
-  my $sth = $dbh->prepare("SELECT * FROM $table WHERE 1=0");
+  my $sth = $dbh->prepare($self->sql_maker->select($table, undef, \'1 = 0'));
   $sth->execute;
   my @columns = @{$sth->{NAME_lc}};
   for my $i ( 0 .. $#columns ){
     my %column_info;
-    my $type_num = $sth->{TYPE}->[$i];
-    my $type_name;
-    if(defined $type_num && $dbh->can('type_info')) {
-      my $type_info = $dbh->type_info($type_num);
-      $type_name = $type_info->{TYPE_NAME} if $type_info;
-    }
-    $column_info{data_type} = $type_name ? $type_name : $type_num;
+    $column_info{data_type} = $sth->{TYPE}->[$i];
     $column_info{size} = $sth->{PRECISION}->[$i];
     $column_info{is_nullable} = $sth->{NULLABLE}->[$i] ? 1 : 0;
 
@@ -1160,6 +1149,18 @@ sub _dbh_columns_info_for {
 
     $result{$columns[$i]} = \%column_info;
   }
+  $sth->finish;
+
+  foreach my $col (keys %result) {
+    my $colinfo = $result{$col};
+    my $type_num = $colinfo->{data_type};
+    my $type_name;
+    if(defined $type_num && $dbh->can('type_info')) {
+      my $type_info = $dbh->type_info($type_num);
+      $type_name = $type_info->{TYPE_NAME} if $type_info;
+      $colinfo->{data_type} = $type_name if $type_name;
+    }
+  }
 
   return \%result;
 }