add money type support
Rafael Kitover [Sat, 25 Jul 2009 20:52:17 +0000 (20:52 +0000)]
lib/DBIx/Class/Storage/DBI/NoBindVars.pm
lib/DBIx/Class/Storage/DBI/Sybase.pm
lib/DBIx/Class/Storage/DBI/Sybase/NoBindVars.pm
t/746mssql.t
t/746sybase.t

index c3ba153..126b9de 100644 (file)
@@ -59,8 +59,11 @@ sub _prep_for_execute {
     foreach my $data (@$bound) {
       $data = ''.$data if ref $data;
 
+      $data = $self->transform_unbound_value($datatype, $data)
+        if $datatype;
+
       $data = $self->_dbh->quote($data)
-        if $self->should_quote_value($datatype, $data);
+        if (!$datatype || $self->should_quote_value($datatype, $data));
 
       $new_sql .= shift(@sql_part) . $data;
     }
@@ -71,7 +74,7 @@ sub _prep_for_execute {
 }
 
 =head2 should_quote_value
-                                
+
 This method is called by L</_prep_for_execute> for every column in
 order to determine if its value should be quoted or not. The arguments
 are the current column data type and the actual bind value. The return
@@ -79,16 +82,25 @@ value is interpreted as: true - do quote, false - do not quote. You should
 override this in you Storage::DBI::<database> subclass, if your RDBMS
 does not like quotes around certain datatypes (e.g. Sybase and integer
 columns). The default method always returns true (do quote).
-                                
+
  WARNING!!!                     
-                                
+
  Always validate that the bind-value is valid for the current datatype.
  Otherwise you may very well open the door to SQL injection attacks.
-                                
+
 =cut                            
-                                
+
 sub should_quote_value { 1 }
 
+=head2 transform_unbound_value
+
+Given a datatype and the value to be inserted directly into a SQL query, returns
+the necessary SQL fragment to represent that value.
+
+=cut
+
+sub transform_unbound_value { $_[2] }
+
 =head1 AUTHORS
 
 Brandon Black <blblack@gmail.com>
index b320242..659aad6 100644 (file)
@@ -245,9 +245,9 @@ sub insert {
 
   my $blob_cols = $self->_remove_blob_cols($source, $to_insert);
 
-# Sybase has nested transactions fortunately, because we have to do the insert
-# in a transaction to avoid race conditions with the SELECT MAX(COL) identity
-# method used when placeholders are enabled.
+# Sybase has savepoints fortunately, because we have to do the insert in a
+# transaction to avoid race conditions with the SELECT MAX(COL) identity method
+# used when placeholders are enabled.
   my $updated_cols = do {
     local $self->{auto_savepoint} = 1;
     my $args = \@_;
index 5053bae..0ca20a4 100644 (file)
@@ -51,6 +51,17 @@ sub should_quote_value {
   return $self->next::method(@_);
 }
 
+sub transform_unbound_value {
+  my ($self, $type, $value) = @_;
+
+  if ($type =~ /money/i && defined $value) {
+    $value =~ s/^\$//;
+    $value = '$' . $value;
+  }
+
+  return $value;
+}
+
 1;
 
 =head1 NAME
index fa8f137..0dbd479 100644 (file)
@@ -33,7 +33,6 @@ $schema->storage->dbh_do (sub {
     my ($storage, $dbh) = @_;
     eval { $dbh->do("DROP TABLE artist") };
     $dbh->do(<<'SQL');
-
 CREATE TABLE artist (
    artistid INT IDENTITY NOT NULL,
    name VARCHAR(100),
@@ -41,7 +40,6 @@ CREATE TABLE artist (
    charfield CHAR(10) NULL,
    primary key(artistid)
 )
-
 SQL
 
 });
@@ -80,14 +78,11 @@ $schema->storage->dbh_do (sub {
     my ($storage, $dbh) = @_;
     eval { $dbh->do("DROP TABLE money_test") };
     $dbh->do(<<'SQL');
-
 CREATE TABLE money_test (
    id INT IDENTITY PRIMARY KEY,
    amount MONEY NULL
 )
-
 SQL
-
 });
 
 my $rs = $schema->resultset('Money');
@@ -116,8 +111,6 @@ $schema->storage->dbh_do (sub {
     eval { $dbh->do("DROP TABLE Owners") };
     eval { $dbh->do("DROP TABLE Books") };
     $dbh->do(<<'SQL');
-
-
 CREATE TABLE Books (
    id INT IDENTITY (1, 1) NOT NULL,
    source VARCHAR(100),
@@ -130,7 +123,6 @@ CREATE TABLE Owners (
    id INT IDENTITY (1, 1) NOT NULL,
    name VARCHAR(100),
 )
-
 SQL
 
 });
@@ -268,11 +260,9 @@ $schema->storage->_sql_maker->{name_sep} = '.';
 
 # clean up our mess
 END {
-    if (my $dbh = eval { $schema->storage->_dbh }) {
-      $dbh->do('DROP TABLE artist');
-      $dbh->do('DROP TABLE money_test');
-      $dbh->do('DROP TABLE Books');
-      $dbh->do('DROP TABLE Owners');
-    }
+  if (my $dbh = eval { $schema->storage->_dbh }) {
+    eval { $dbh->do("DROP TABLE $_") }
+      for qw/artist money_test Books Owners/;
+  }
 }
 # vim:sw=2 sts=2
index 6d77b4e..e94da41 100644 (file)
@@ -9,7 +9,7 @@ use DBICTest;
 
 my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_SYBASE_${_}" } qw/DSN USER PASS/};
 
-my $TESTS = 29 + 2;
+my $TESTS = 35 + 2;
 
 if (not ($dsn && $user)) {
   plan skip_all =>
@@ -76,7 +76,7 @@ SQL
 # so we start unconnected
   $schema->storage->disconnect;
 
-# inserts happen in a txn, so we test txn nesting
+# inserts happen in a txn, so we make sure they can nest
   $schema->txn_begin;
 
 # test primary key handling
@@ -222,12 +222,51 @@ SQL
     diag $@ if $@;
     ok($got eq $new_str, "verified updated blob");
   }
+
+# test MONEY column support
+  $schema->storage->dbh_do (sub {
+      my ($storage, $dbh) = @_;
+      eval { $dbh->do("DROP TABLE money_test") };
+      $dbh->do(<<'SQL');
+CREATE TABLE money_test (
+   id INT IDENTITY PRIMARY KEY,
+   amount MONEY NULL
+)
+SQL
+  });
+
+  my $rs = $schema->resultset('Money');
+
+  my $row;
+  lives_ok {
+    $row = $rs->create({ amount => 100 });
+  } 'inserted a money value';
+
+  is eval { $rs->find($row->id)->amount }, 100, 'money value round-trip';
+
+  lives_ok {
+    $row->update({ amount => 200 });
+  } 'updated a money value';
+
+  is eval { $rs->find($row->id)->amount },
+    200, 'updated money value round-trip';
+
+  lives_ok {
+    $row->update({ amount => undef });
+  } 'updated a money value to NULL';
+
+  my $null_amount = eval { $rs->find($row->id)->amount };
+  ok(
+    (($null_amount == undef) && (not $@)),
+    'updated money value to NULL round-trip'
+  );
+  diag $@ if $@;
 }
 
 # clean up our mess
 END {
   if (my $dbh = eval { $schema->storage->_dbh }) {
-    $dbh->do('DROP TABLE artist');
-    eval { $dbh->do('DROP TABLE bindtype_test')    };
+    eval { $dbh->do("DROP TABLE $_") }
+      for qw/artist bindtype_test money_test/;
   }
 }