fast-path populate inserts incorrect data

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

fast-path populate inserts incorrect data

Andrew Gregory
_dbh_execute_for_fetch handles some columns (e.g. MONEY columns with
MS SQL Server) incorrectly.  For columns that are casted during
insertion, the column data is not in the format _dbh_execute_for_fetch
expects.  It tries to extract the _bind_data_slice_idx field, but it
is located inside a second list element like so:

 [
   {
     'dbic_colname' => 'money_col',
     'sqlt_datatype' => 'MONEY'
   },
   \[
     '?',
     [
       {
         '_bind_data_slice_idx' => 0,
         'dbic_colname' => 'money_col'
       },
       '0.50'
     ]
   ]
 ],

Instead of the appropriate index, it gets undef, which it then uses as
an index into the row values, causing it to incorrectly insert the
first value in the row for all of these columns.

The following patch appears to fix the issue, but I'm not nearly
familiar enough with the codebase to know if it's the correct
solution:

diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm
index 9600389b..40d71eeb 100644
--- a/lib/DBIx/Class/Storage/DBI.pm
+++ b/lib/DBIx/Class/Storage/DBI.pm
@@ -2317,7 +2317,7 @@ sub _dbh_execute_for_fetch {
         ? "$v"
         : $v
       ;
-    } map { $_->[0] } @$proto_bind ];
+    } map { ref $_->[1] ? ${$_->[1]}->[1][0] : $_->[0] } @$proto_bind ];
   };
 
   my $tuple_status = [];

Finally, here is a test case to replicate the issue:

#!/usr/bin/perl

use strict;
use warnings;

package FPP::Schema::Result::Table;
use parent 'DBIx::Class::Core';

__PACKAGE__->table('dummy');
__PACKAGE__->add_columns(
    rowid  => { data_type => 'INT',   is_numeric => 1 },
    amount => { data_type => 'MONEY', is_numeric => 1 },
);
__PACKAGE__->set_primary_key('rowid');

package FPP::Schema;
use parent 'DBIx::Class::Schema';

# force MSSQL syntax which uses a CAST when inserting MONEY values
__PACKAGE__->storage_type('::DBI::MSSQL');
__PACKAGE__->register_source( 'Table',
    FPP::Schema::Result::Table->result_source_instance );

package main;

# SQLite's syntax is flexible enough to accommodate SQL Server statements
my $schema = FPP::Schema->connect('dbi:SQLite:dbname=:memory:');
$schema->deploy();

$schema->storage->debug(1);

# fast-path populate; note the warning about an uninitialized value
$schema->resultset('Table')->populate( [ [qw( rowid amount )], [ 1, 250 ] ] );

# slow-path populate
scalar $schema->resultset('Table')
  ->populate( [ [qw( rowid amount )], [ 2, 150 ] ] );

# table should look like:
#  rowid amount
#      1    250
#      2    150
#
# instead, we get:
#  rowid amount
#      1      1
#      2    150

foreach my $row ( $schema->resultset('Table')->all ) {
    print 'rowid: ',  $row->rowid,  "\n";
    print 'amount: ', $row->amount, "\n";
}

_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...