From: Rafael Kitover Date: Sun, 13 Sep 2009 09:52:20 +0000 (+0000) Subject: make sure _init doesn't loop, steal insert_bulk from mssql, add some insert_bulk... X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=40531ea8279448b1379db93a4f86e78e8393e8a8;p=dbsrgits%2FDBIx-Class-Historic.git make sure _init doesn't loop, steal insert_bulk from mssql, add some insert_bulk tests --- diff --git a/lib/DBIx/Class/Storage/DBI/Sybase.pm b/lib/DBIx/Class/Storage/DBI/Sybase.pm index 6911e79..e86e679 100644 --- a/lib/DBIx/Class/Storage/DBI/Sybase.pm +++ b/lib/DBIx/Class/Storage/DBI/Sybase.pm @@ -13,10 +13,11 @@ use List::Util (); use Sub::Name (); __PACKAGE__->mk_group_accessors('simple' => - qw/_identity _blob_log_on_update _insert_storage _identity_method/ + qw/_identity _blob_log_on_update _insert_storage _is_insert_storage + _identity_method/ ); -my @delegate_to_insert_storage = qw/ +my @also_proxy_to_insert_storage = qw/ disconnect _connect_info _sql_maker _sql_maker_opts disable_sth_caching auto_savepoint unsafe cursor_class debug debugobj schema /; @@ -115,17 +116,21 @@ sub _init { # based on LongReadLen in connect_info $self->set_textsize if $self->using_freetds; -# create storage for insert transactions - $self->_insert_storage((ref $self)->new); - $self->_insert_storage->connect_info($self->connect_info); +# create storage for insert transactions, unless this is that storage + return if $self->_is_insert_storage; - $self->_insert_storage->set_textsize if $self->using_freetds; + my $insert_storage = (ref $self)->new; + + $insert_storage->_is_insert_storage(1); + $insert_storage->connect_info($self->connect_info); + + $self->_insert_storage($insert_storage); } -for my $method (@delegate_to_insert_storage) { +for my $method (@also_proxy_to_insert_storage) { no strict 'refs'; - *{$method} = Sub::Name::subname $method => sub { + *{$method} = Sub::Name::subname __PACKAGE__."::$method" => sub { my $self = shift; $self->_insert_storage->$method(@_) if $self->_insert_storage; return $self->next::method(@_); @@ -376,6 +381,63 @@ sub update { return $wantarray ? @res : $res[0]; } +### the insert_bulk stuff stolen from DBI/MSSQL.pm + +sub _set_identity_insert { + my ($self, $table) = @_; + + my $sql = sprintf ( + 'SET IDENTITY_INSERT %s ON', + $self->sql_maker->_quote ($table), + ); + + my $dbh = $self->_get_dbh; + eval { $dbh->do ($sql) }; + if ($@) { + $self->throw_exception (sprintf "Error executing '%s': %s", + $sql, + $dbh->errstr, + ); + } +} + +sub _unset_identity_insert { + my ($self, $table) = @_; + + my $sql = sprintf ( + 'SET IDENTITY_INSERT %s OFF', + $self->sql_maker->_quote ($table), + ); + + my $dbh = $self->_get_dbh; + $dbh->do ($sql); +} + +# XXX this should use the DBD::Sybase bulk API, where possible +sub insert_bulk { + my $self = shift; + my ($source, $cols, $data) = @_; + + my $is_identity_insert = (List::Util::first + { $source->column_info ($_)->{is_auto_increment} } + (@{$cols}) + ) + ? 1 + : 0; + + if ($is_identity_insert) { + $self->_set_identity_insert ($source->name); + } + + $self->next::method(@_); + + if ($is_identity_insert) { + $self->_unset_identity_insert ($source->name); + } +} + +### end of stolen insert_bulk section + sub _remove_blob_cols { my ($self, $source, $fields) = @_; diff --git a/t/746sybase.t b/t/746sybase.t index 4594429..516d839 100644 --- a/t/746sybase.t +++ b/t/746sybase.t @@ -11,7 +11,7 @@ use DBIx::Class::Storage::DBI::Sybase::NoBindVars; my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_SYBASE_${_}" } qw/DSN USER PASS/}; -my $TESTS = 41 + 2; +my $TESTS = 48 + 2; if (not ($dsn && $user)) { plan skip_all => @@ -188,6 +188,71 @@ SQL } } +# test insert_bulk using populate, this should always pass whether or not it +# does anything Sybase specific or not. Just here to aid debugging. + lives_ok { + $schema->resultset('Artist')->populate([ + { + name => 'bulk artist 1', + charfield => 'foo', + }, + { + name => 'bulk artist 2', + charfield => 'foo', + }, + { + name => 'bulk artist 3', + charfield => 'foo', + }, + ]); + } 'insert_bulk via populate'; + + my $bulk_rs = $schema->resultset('Artist')->search({ + name => { -like => 'bulk artist %' } + }); + + is $bulk_rs->count, 3, 'correct number inserted via insert_bulk'; + + is ((grep $_->charfield eq 'foo', $bulk_rs->all), 3, + 'column set correctly via insert_bulk'); + + my %bulk_ids; + @bulk_ids{map $_->artistid, $bulk_rs->all} = (); + + is ((scalar keys %bulk_ids), 3, + 'identities generated correctly in insert_bulk'); + + $bulk_rs->delete; + +# now test insert_bulk with IDENTITY_INSERT + lives_ok { + $schema->resultset('Artist')->populate([ + { + artistid => 2001, + name => 'bulk artist 1', + charfield => 'foo', + }, + { + artistid => 2002, + name => 'bulk artist 2', + charfield => 'foo', + }, + { + artistid => 2003, + name => 'bulk artist 3', + charfield => 'foo', + }, + ]); + } 'insert_bulk with IDENTITY_INSERT via populate'; + + is $bulk_rs->count, 3, + 'correct number inserted via insert_bulk with IDENTITY_INSERT'; + + is ((grep $_->charfield eq 'foo', $bulk_rs->all), 3, + 'column set correctly via insert_bulk with IDENTITY_INSERT'); + + $bulk_rs->delete; + # test correlated subquery my $subq = $schema->resultset('Artist')->search({ artistid => { '>' => 3 } }) ->get_column('artistid')