deploy debugging and drop table statements
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI.pm
index b87648a..f6e682d 100644 (file)
@@ -1,5 +1,7 @@
 package DBIx::Class::Storage::DBI;
 
+use base 'DBIx::Class::Storage';
+
 use strict;
 use warnings;
 use DBI;
@@ -66,6 +68,8 @@ sub _order_by {
     if (defined $_[0]->{order_by}) {
       $ret .= $self->SUPER::_order_by($_[0]->{order_by});
     }
+  } elsif(ref $_[0] eq 'SCALAR') {
+    $ret = $self->_sqlcase(' order by ').${ $_[0] };
   } else {
     $ret = $self->SUPER::_order_by(@_);
   }
@@ -301,8 +305,10 @@ sub ensure_connected {
 sub dbh {
   my ($self) = @_;
 
-  $self->_dbh(undef)
-    if $self->_connection_pid && $self->_connection_pid != $$;
+  if($self->_connection_pid && $self->_connection_pid != $$) {
+      $self->_dbh->{InactiveDestroy} = 1;
+      $self->_dbh(undef)
+  }
   $self->ensure_connected;
   return $self->_dbh;
 }
@@ -335,17 +341,25 @@ sub _populate_dbh {
 sub _connect {
   my ($self, @info) = @_;
 
+  my ($old_connect_via, $dbh);
+
   if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) {
-      my $old_connect_via = $DBI::connect_via;
+      $old_connect_via = $DBI::connect_via;
       $DBI::connect_via = 'connect';
-      my $dbh = DBI->connect(@info);
-      $DBI::connect_via = $old_connect_via;
-      return $dbh;
   }
 
-  my $dbh = DBI->connect(@info);
+  if(ref $info[0] eq 'CODE') {
+      $dbh = &{$info[0]};
+  }
+  else {
+      $dbh = DBI->connect(@info);
+  }
+
+  $DBI::connect_via = $old_connect_via if $old_connect_via;
+
   $self->throw_exception("DBI Connection failed: $DBI::errstr")
       unless $dbh;
+
   $dbh;
 }
 
@@ -353,11 +367,15 @@ sub _connect {
 
 Calls begin_work on the current dbh.
 
+See L<DBIx::Class::Schema> for the txn_do() method, which allows for
+an entire code block to be executed transactionally.
+
 =cut
 
 sub txn_begin {
   my $self = shift;
-  $self->dbh->begin_work if $self->{transaction_depth}++ == 0 and $self->dbh->{AutoCommit};
+  $self->dbh->begin_work
+    if $self->{transaction_depth}++ == 0 and $self->dbh->{AutoCommit};
 }
 
 =head2 txn_commit
@@ -378,17 +396,32 @@ sub txn_commit {
 
 =head2 txn_rollback
 
-Issues a rollback against the current dbh.
+Issues a rollback against the current dbh. A nested rollback will
+throw a L<DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION> exception,
+which allows the rollback to propagate to the outermost transaction.
 
 =cut
 
 sub txn_rollback {
   my $self = shift;
-  if ($self->{transaction_depth} == 0) {
-    $self->dbh->rollback unless $self->dbh->{AutoCommit};
-  }
-  else {
-    --$self->{transaction_depth} == 0 ? $self->dbh->rollback : die $@;    
+
+  eval {
+    if ($self->{transaction_depth} == 0) {
+      $self->dbh->rollback unless $self->dbh->{AutoCommit};
+    }
+    else {
+      --$self->{transaction_depth} == 0 ?
+        $self->dbh->rollback :
+       die DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->new;
+    }
+  };
+
+  if ($@) {
+    my $error = $@;
+    my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
+    $error =~ /$exception_class/ and $self->throw_exception($error);
+    $self->{transaction_depth} = 0;          # ensure that a failed rollback
+    $self->throw_exception($error);          # resets the transaction depth
   }
 }
 
@@ -397,15 +430,15 @@ sub _execute {
   my ($sql, @bind) = $self->sql_maker->$op($ident, @args);
   unshift(@bind, @$extra_bind) if $extra_bind;
   if ($self->debug) {
-      my @debug_bind = map { defined $_ ? $_ : 'NULL' } @bind;
-      $self->debugfh->print("$sql: @debug_bind\n");
+      my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind;
+      $self->debugfh->print("$sql: " . join(', ', @debug_bind) . "\n");
   }
   my $sth = $self->sth($sql,$op);
   $self->throw_exception("no sth generated via sql: $sql") unless $sth;
   @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args
   my $rv;
   if ($sth) {  
-    $rv = $sth->execute(@bind);
+    $rv = $sth->execute(@bind) or $self->throw_exception("Error executing '$sql': " . $sth->errstr);
   } else { 
     $self->throw_exception("'$sql' did not generate a statement.");
   }
@@ -507,12 +540,7 @@ sub last_insert_id {
 
 }
 
-sub sqlt_type {
-  my ($self) = @_;
-  my $dsn = $self->connect_info->[0];
-  $dsn =~ /^dbi:(.*?)\d*:/;
-  return $1;
-}
+sub sqlt_type { shift->dbh->{Driver}->{Name} }
 
 sub deployment_statements {
   my ($self, $schema, $type) = @_;
@@ -523,7 +551,23 @@ sub deployment_statements {
   $self->throw_exception($@) if $@; 
   eval "use SQL::Translator::Producer::${type};";
   $self->throw_exception($@) if $@;
-  my $tr = SQL::Translator->new();
+  my $tr = SQL::Translator->new(
+      # Print debug info
+      debug               => 1,
+      # Print Parse::RecDescent trace
+      trace               => 0,
+      # Don't include comments in output
+      no_comments         => 1,
+      # Print name mutations, conflicts
+      show_warnings       => 0,
+      # Add "drop table" statements
+      add_drop_table      => 1,
+      # Validate schema object
+      validate            => 1,
+      # Make all table names CAPS in producers which support this option
+      format_table_name   => sub {my $tablename = shift; return uc($tablename)},
+  );
+
   SQL::Translator::Parser::DBIx::Class::parse( $tr, $schema );
   return "SQL::Translator::Producer::${type}"->can('produce')->($tr);
 }
@@ -531,6 +575,7 @@ sub deployment_statements {
 sub deploy {
   my ($self, $schema, $type) = @_;
   foreach(split(";\n", $self->deployment_statements($schema, $type))) {
+      $self->debugfh->print("$_\n") if $self->debug;
          $self->dbh->do($_) or warn "SQL was:\n $_";
   } 
 }