A couple of typos, and general whitespace cleanup (ick)
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Oracle / Generic.pm
index 87715ed..ac1a3a1 100644 (file)
@@ -1,5 +1,4 @@
 package DBIx::Class::Storage::DBI::Oracle::Generic;
-# -*- mode: cperl; cperl-indent-level: 2 -*-
 
 use strict;
 use warnings;
@@ -24,12 +23,11 @@ This class implements autoincrements for Oracle.
 
 =cut
 
-use Carp::Clan qw/^DBIx::Class/;
-use Scalar::Util ();
+use base qw/DBIx::Class::Storage::DBI/;
+use mro 'c3';
 
-use base qw/DBIx::Class::Storage::DBI::MultiDistinctEmulation/;
-
-# __PACKAGE__->load_components(qw/PK::Auto/);
+# For ORA_BLOB => 113, ORA_CLOB => 112
+use DBD::Oracle qw( :ora_types );
 
 sub _dbh_last_insert_id {
   my ($self, $dbh, $source, @columns) = @_;
@@ -85,28 +83,18 @@ sub _sequence_fetch {
   return $id;
 }
 
-sub connected {
+sub _ping {
   my $self = shift;
-  
-  if ($self->SUPER::connected(@_)) {
-      my $dbh = $self->_dbh;
-  
-      my $ping_sth = $dbh->prepare_cached("select 1 from dual");
-  
-      local $dbh->{RaiseError} = 1;
-      eval {
-          $ping_sth->execute;
-          $ping_sth->finish;
-      };
-  
-      if ($@) {
-          return 0;
-      } else {
-          return 1;
-      }
-  }
-  
-  return 0;
+
+  my $dbh = $self->_dbh or return 0;
+
+  local $dbh->{RaiseError} = 1;
+
+  eval {
+    $dbh->do("select 1 from dual");
+  };
+
+  return $@ ? 0 : 1;
 }
 
 sub _dbh_execute {
@@ -114,28 +102,28 @@ sub _dbh_execute {
   my ($dbh, $op, $extra_bind, $ident, $bind_attributes, @args) = @_;
 
   my $wantarray = wantarray;
-  my @res;
-  my $exception;
-
-  my $try = 2;
-  
-  while ($try--) {
-    eval {
-      if ($wantarray) {
-        @res    = $self->SUPER::_dbh_execute(@_);
+
+  my (@res, $exception, $retried);
+
+  RETRY: {
+    do {
+      eval {
+        if ($wantarray) {
+          @res    = $self->next::method(@_);
+        } else {
+          $res[0] = $self->next::method(@_);
+        }
+      };
+      $exception = $@;
+      if ($exception =~ /ORA-01003/) {
+        # ORA-01003: no statement parsed (someone changed the table somehow,
+        # invalidating your cursor.)
+        my ($sql, $bind) = $self->_prep_for_execute($op, $extra_bind, $ident, \@args);
+        delete $dbh->{CachedKids}{$sql};
       } else {
-        $res[0] = $self->SUPER::_dbh_execute(@_);
+        last RETRY;
       }
-    };
-    $exception = $@;
-    if ($exception =~ /ORA-01003/) {
-# ORA-01003: no statement parsed (someone changed the table somehow,
-# invalidating your cursor.)
-      my ($sql, $bind) = $self->_prep_for_execute($op, $extra_bind, $ident, \@args);
-      delete $dbh->{CachedKids}{$sql};
-    } else {
-      last;
-    }
+    } while (not $retried++);
   }
 
   $self->throw_exception($exception) if $exception;
@@ -151,7 +139,7 @@ Returns the sequence name for an autoincrement column
 
 sub get_autoinc_seq {
   my ($self, $source, $col) = @_;
-    
+
   $self->dbh_do('_dbh_get_autoinc_seq', $source, $col);
 }
 
@@ -177,12 +165,97 @@ L<DBIx::Class::InflateColumn::DateTime>.
 
 sub datetime_parser_type { return "DateTime::Format::Oracle"; }
 
+=head2 connect_call_datetime_setup
+
+Used as:
+
+    on_connect_call => 'datetime_setup'
+
+In L<DBIx::Class::Storage::DBI/connect_info> to set the session nls date, and
+timestamp values for use with L<DBIx::Class::InflateColumn::DateTime> and the
+necessary environment variables for L<DateTime::Format::Oracle>, which is used
+by it.
+
+Maximum allowable precision is used, unless the environment variables have
+already been set.
+
+These are the defaults used:
+
+  $ENV{NLS_DATE_FORMAT}         ||= 'YYYY-MM-DD HH24:MI:SS';
+  $ENV{NLS_TIMESTAMP_FORMAT}    ||= 'YYYY-MM-DD HH24:MI:SS.FF';
+  $ENV{NLS_TIMESTAMP_TZ_FORMAT} ||= 'YYYY-MM-DD HH24:MI:SS.FF TZHTZM';
+
+To get more than second precision with L<DBIx::Class::InflateColumn::DateTime>
+for your timestamps, use something like this:
+
+  use Time::HiRes 'time';
+  my $ts = DateTime->from_epoch(epoch => time);
+
+=cut
+
+sub connect_call_datetime_setup {
+  my $self = shift;
+  my $dbh  = $self->dbh;
+
+  my $date_format = $ENV{NLS_DATE_FORMAT} ||= 'YYYY-MM-DD HH24:MI:SS';
+  my $timestamp_format = $ENV{NLS_TIMESTAMP_FORMAT} ||=
+    'YYYY-MM-DD HH24:MI:SS.FF';
+  my $timestamp_tz_format = $ENV{NLS_TIMESTAMP_TZ_FORMAT} ||=
+    'YYYY-MM-DD HH24:MI:SS.FF TZHTZM';
+
+  $dbh->do("alter session set nls_date_format = '$date_format'");
+  $dbh->do("alter session set nls_timestamp_format = '$timestamp_format'");
+  $dbh->do("alter session set nls_timestamp_tz_format='$timestamp_tz_format'");
+}
+
 sub _svp_begin {
     my ($self, $name) = @_;
+
     $self->dbh->do("SAVEPOINT $name");
 }
 
+=head2 source_bind_attributes
+
+Handle LOB types in Oracle.  Under a certain size (4k?), you can get away
+with the driver assuming your input is the deprecated LONG type if you
+encode it as a hex string.  That ain't gonna fly at larger values, where
+you'll discover you have to do what this does.
+
+This method had to be overridden because we need to set ora_field to the
+actual column, and that isn't passed to the call (provided by Storage) to
+bind_attribute_by_data_type.
+
+According to L<DBD::Oracle>, the ora_field isn't always necessary, but
+adding it doesn't hurt, and will save your bacon if you're modifying a
+table with more than one LOB column.
+
+=cut
+
+sub source_bind_attributes 
+{
+       my $self = shift;
+       my($source) = @_;
+
+       my %bind_attributes;
+
+       foreach my $column ($source->columns) {
+               my $data_type = $source->column_info($column)->{data_type} || '';
+               next unless $data_type;
+
+               my %column_bind_attrs = $self->bind_attribute_by_data_type($data_type);
+
+               if ($data_type =~ /^[BC]LOB$/i) {
+                       $column_bind_attrs{'ora_type'}
+                               = uc($data_type) eq 'CLOB' ? ORA_CLOB : ORA_BLOB;
+                       $column_bind_attrs{'ora_field'} = $column;
+               }
+
+               $bind_attributes{$column} = \%column_bind_attrs;
+       }
+
+       return \%bind_attributes;
+}
+
 # Oracle automatically releases a savepoint when you start another one with the
 # same name.
 sub _svp_release { 1 }