cleanup cursor class handling
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI.pm
index ab28f83..5be636f 100644 (file)
@@ -13,10 +13,12 @@ use Scalar::Util qw/blessed weaken/;
 
 __PACKAGE__->mk_group_accessors('simple' =>
     qw/_connect_info _dbi_connect_info _dbh _sql_maker _sql_maker_opts
-       _conn_pid _conn_tid disable_sth_caching cursor on_connect_do
-       transaction_depth unsafe/
+       _conn_pid _conn_tid disable_sth_caching on_connect_do
+       transaction_depth unsafe _dbh_autocommit/
 );
 
+__PACKAGE__->cursor_class('DBIx::Class::Storage::DBI::Cursor');
+
 BEGIN {
 
 package DBIC::SQL::Abstract; # Would merge upstream, but nate doesn't reply :(
@@ -115,7 +117,7 @@ sub _emulate_limit {
 }
 
 sub _recurse_fields {
-  my ($self, $fields) = @_;
+  my ($self, $fields, $params) = @_;
   my $ref = ref $fields;
   return $self->_quote($fields) unless $ref;
   return $$fields if $ref eq 'SCALAR';
@@ -123,10 +125,10 @@ sub _recurse_fields {
   if ($ref eq 'ARRAY') {
     return join(', ', map {
       $self->_recurse_fields($_)
-      .(exists $self->{rownum_hack_count}
-         ? ' AS col'.$self->{rownum_hack_count}++
-         : '')
-     } @$fields);
+        .(exists $self->{rownum_hack_count} && !($params && $params->{no_rownum_hack})
+          ? ' AS col'.$self->{rownum_hack_count}++
+          : '')
+      } @$fields);
   } elsif ($ref eq 'HASH') {
     foreach my $func (keys %$fields) {
       return $self->_sqlcase($func)
@@ -142,7 +144,7 @@ sub _order_by {
   if (ref $_[0] eq 'HASH') {
     if (defined $_[0]->{group_by}) {
       $ret = $self->_sqlcase(' group by ')
-               .$self->_recurse_fields($_[0]->{group_by});
+        .$self->_recurse_fields($_[0]->{group_by}, { no_rownum_hack => 1 });
     }
     if (defined $_[0]->{having}) {
       my $frag;
@@ -311,7 +313,6 @@ documents DBI-specific methods and behaviors.
 sub new {
   my $new = shift->next::method(@_);
 
-  $new->cursor("DBIx::Class::Storage::DBI::Cursor");
   $new->transaction_depth(0);
   $new->_sql_maker_opts({});
   $new->{_in_dbh_do} = 0;
@@ -479,6 +480,7 @@ sub connect_info {
 
   my $last_info = $dbi_info->[-1];
   if(ref $last_info eq 'HASH') {
+    $last_info = { %$last_info }; # so delete is non-destructive
     for my $storage_opt (qw/on_connect_do disable_sth_caching unsafe/) {
       if(my $value = delete $last_info->{$storage_opt}) {
         $self->$storage_opt($value);
@@ -489,6 +491,8 @@ sub connect_info {
         $self->_sql_maker_opts->{$sql_maker_opt} = $opt_val;
       }
     }
+    # re-insert modified hashref
+    $dbi_info->[-1] = $last_info;
 
     # Get rid of any trailing empty hashref
     pop(@$dbi_info) if !keys %$last_info;
@@ -582,6 +586,8 @@ sub txn_do {
   ref $coderef eq 'CODE' or $self->throw_exception
     ('$coderef must be a CODE reference');
 
+  return $coderef->(@_) if $self->{transaction_depth};
+
   local $self->{_in_dbh_do} = 1;
 
   my @result;
@@ -642,7 +648,7 @@ sub disconnect {
   my ($self) = @_;
 
   if( $self->connected ) {
-    $self->_dbh->rollback unless $self->_dbh->{AutoCommit};
+    $self->_dbh->rollback unless $self->_dbh_autocommit;
     $self->_dbh->disconnect;
     $self->_dbh(undef);
     $self->{_dbh_gen}++;
@@ -723,7 +729,7 @@ sub _populate_dbh {
 
   # Always set the transaction depth on connect, since
   #  there is no transaction in progress by definition
-  $self->{transaction_depth} = $self->_dbh->{AutoCommit} ? 0 : 1;
+  $self->{transaction_depth} = $self->_dbh_autocommit ? 0 : 1;
 
   if(ref $self eq 'DBIx::Class::Storage::DBI') {
     my $driver = $self->_dbh->{Driver}->{Name};
@@ -782,6 +788,8 @@ sub _connect {
   $self->throw_exception("DBI Connection failed: " . ($@||$DBI::errstr))
     if !$dbh || $@;
 
+  $self->_dbh_autocommit($dbh->{AutoCommit});
+
   $dbh;
 }
 
@@ -789,7 +797,7 @@ sub _connect {
 sub txn_begin {
   my $self = shift;
   $self->ensure_connected();
-  if($self->{transaction_depth}++ == 0) {
+  if($self->{transaction_depth} == 0) {
     $self->debugobj->txn_begin()
       if $self->debug;
     # this isn't ->_dbh-> because
@@ -797,6 +805,7 @@ sub txn_begin {
     #  for AutoCommit users
     $self->dbh->begin_work;
   }
+  $self->{transaction_depth}++;
 }
 
 sub txn_commit {
@@ -807,7 +816,7 @@ sub txn_commit {
       if ($self->debug);
     $dbh->commit;
     $self->{transaction_depth} = 0
-      if $dbh->{AutoCommit};
+      if $self->_dbh_autocommit;
   }
   elsif($self->{transaction_depth} > 1) {
     $self->{transaction_depth}--
@@ -817,15 +826,13 @@ sub txn_commit {
 sub txn_rollback {
   my $self = shift;
   my $dbh = $self->_dbh;
-  my $autocommit;
   eval {
-    $autocommit = $dbh->{AutoCommit};
     if ($self->{transaction_depth} == 1) {
       $self->debugobj->txn_rollback()
         if ($self->debug);
-      $dbh->rollback;
       $self->{transaction_depth} = 0
-        if $autocommit;
+        if $self->_dbh_autocommit;
+      $dbh->rollback;
     }
     elsif($self->{transaction_depth} > 1) {
       $self->{transaction_depth}--;
@@ -839,7 +846,7 @@ sub txn_rollback {
     my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
     $error =~ /$exception_class/ and $self->throw_exception($error);
     # ensure that a failed rollback resets the transaction depth
-    $self->{transaction_depth} = $autocommit ? 0 : 1;
+    $self->{transaction_depth} = $self->_dbh_autocommit ? 0 : 1;
     $self->throw_exception($error);
   }
 }
@@ -1055,7 +1062,7 @@ Handle a SQL select statement.
 sub select {
   my $self = shift;
   my ($ident, $select, $condition, $attrs) = @_;
-  return $self->cursor->new($self, \@_, $attrs);
+  return $self->cursor_class->new($self, \@_, $attrs);
 }
 
 sub select_single {
@@ -1200,7 +1207,7 @@ sub bind_attribute_by_data_type {
     return;
 }
 
-=head2 create_ddl_dir (EXPERIMENTAL)
+=head2 create_ddl_dir
 
 =over 4
 
@@ -1211,9 +1218,6 @@ sub bind_attribute_by_data_type {
 Creates a SQL file based on the Schema, for each of the specified
 database types, in the given directory.
 
-Note that this feature is currently EXPERIMENTAL and may not work correctly
-across all databases, or fully handle complex relationships.
-
 =cut
 
 sub create_ddl_dir
@@ -1431,7 +1435,10 @@ Returns the datetime parser class
 
 sub datetime_parser {
   my $self = shift;
-  return $self->{datetime_parser} ||= $self->build_datetime_parser(@_);
+  return $self->{datetime_parser} ||= do {
+    $self->ensure_connected;
+    $self->build_datetime_parser(@_);
+  };
 }
 
 =head2 datetime_parser_type