From: Marc Mims Date: Wed, 2 Jan 2008 22:24:23 +0000 (+0000) Subject: Added Storage::DBI subclass for MSSQL auto PK over ODBC. X-Git-Tag: v0.08010~22 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=c1cac6332247a092ddc886c52607b24104c3fb46;p=dbsrgits%2FDBIx-Class.git Added Storage::DBI subclass for MSSQL auto PK over ODBC. --- diff --git a/Changes b/Changes index e008519..bee681a 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,6 @@ Revision history for DBIx::Class + - Added Storage::DBI subclass for MSSQL over ODBC. - Added freeze, thaw and dclone methods to Schema so that thawed objects will get re-attached to the schema. - Moved dbicadmin to JSON::Any wrapped JSON.pm for a sane API diff --git a/lib/DBIx/Class/Storage/DBI/ODBC/Microsoft_SQL_Server.pm b/lib/DBIx/Class/Storage/DBI/ODBC/Microsoft_SQL_Server.pm new file mode 100644 index 0000000..88eeab6 --- /dev/null +++ b/lib/DBIx/Class/Storage/DBI/ODBC/Microsoft_SQL_Server.pm @@ -0,0 +1,76 @@ +package DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server; +use strict; +use warnings; + +use base qw/DBIx::Class::Storage::DBI/; + +sub _prep_for_execute { + my $self = shift; + my ($op, $extra_bind, $ident, $args) = @_; + + my ($sql, $bind) = $self->SUPER::_prep_for_execute(@_); + $sql .= ';SELECT SCOPE_IDENTITY()' if $op eq 'insert'; + + return ($sql, $bind); +} + +sub insert { + my ($self, $source, $to_insert) = @_; + + my $bind_attributes = $self->source_bind_attributes($source); + my (undef, $sth) = $self->_execute( 'insert' => [], $source, $bind_attributes, $to_insert); + $self->{_scope_identity} = $sth->fetchrow_array; + + return $to_insert; +} + +sub last_insert_id { shift->{_scope_identity} } + +sub sqlt_type { 'SQLServer' } + +sub _sql_maker_opts { + my ( $self, $opts ) = @_; + + if ( $opts ) { + $self->{_sql_maker_opts} = { %$opts }; + } + + return { limit_dialect => 'Top', %{$self->{_sql_maker_opts}||{}} }; +} + +1; + +__END__ + +=head1 NAME + +DBIx::Class::Storage::ODBC::Microsoft_SQL_Server - Support specific to +Microsoft SQL Server over ODBC + +=head1 DESCRIPTION + +This class implements support specific to Microsoft SQL Server over ODBC, +including auto-increment primary keys and SQL::Abstract::Limit dialect. It +is loaded automatically by by DBIx::Class::Storage::DBI::ODBC when it +detects a MSSQL back-end. + +=head1 IMPLEMENTATION NOTES + +Microsoft SQL Server supports three methods of retrieving the IDENTITY +value for inserted row: IDENT_CURRENT, @@IDENTITY, and SCOPE_IDENTITY(). +SCOPE_IDENTITY is used here because it is the safest. However, it must +be called is the same execute statement, not just the same connection. + +So, this implementation appends a SELECT SCOPE_IDENTITY() statement +onto each INSERT to accommodate that requirement. + + +=head1 AUTHORS + +Marc Mims C<< >> + +=head1 LICENSE + +You may distribute this code under the same terms as Perl itself. + +=cut diff --git a/t/746db2_400.t b/t/746db2_400.t index 9ebe7cc..e784189 100644 --- a/t/746db2_400.t +++ b/t/746db2_400.t @@ -21,7 +21,7 @@ my $schema = DBICTest::Schema->connect($dsn, $user, $pass); my $dbh = $schema->storage->dbh; -$dbh->do("DROP TABLE artist", { RaiseError => 0, PrintError => 0 }); +eval { $dbh->do("DROP TABLE artist") }; $dbh->do("CREATE TABLE artist (artistid INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), name VARCHAR(255), charfield CHAR(10))"); diff --git a/t/746mssql.t b/t/746mssql.t new file mode 100644 index 0000000..52b5357 --- /dev/null +++ b/t/746mssql.t @@ -0,0 +1,63 @@ +use strict; +use warnings; + +use Test::More; +use lib qw(t/lib); +use DBICTest; + +my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MSSQL_ODBC_${_}" } qw/DSN USER PASS/}; + +plan skip_all => 'Set $ENV{DBICTEST_MSSQL_ODBC_DSN}, _USER and _PASS to run this test' + unless ($dsn && $user); + +plan tests => 12; + +my $schema = DBICTest::Schema->connect($dsn, $user, $pass, {AutoCommit => 1}); + +$schema->storage->ensure_connected; +isa_ok( $schema->storage, 'DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server' ); + +my $dbh = $schema->storage->dbh; + +eval { $dbh->do("DROP TABLE artist") }; + + $dbh->do(<<''); +CREATE TABLE artist ( + artistid INT IDENTITY NOT NULL, + name VARCHAR(255), + charfield CHAR(10), + primary key(artistid) +) + +my %seen_id; + +# test primary key handling +my $new = $schema->resultset('Artist')->create({ name => 'foo' }); +ok($new->artistid > 0, "Auto-PK worked"); + +$seen_id{$new->artistid}++; + +# test LIMIT support +for (1..6) { + $new = $schema->resultset('Artist')->create({ name => 'Artist ' . $_ }); + is ( $seen_id{$new->artistid}, undef, "id for Artist $_ is unique" ); + $seen_id{$new->artistid}++; +} + +my $it = $schema->resultset('Artist')->search( {}, { + rows => 3, + order_by => 'artistid', +}); + +is( $it->count, 3, "LIMIT count ok" ); +is( $it->next->name, "foo", "iterator->next ok" ); +$it->next; +is( $it->next->name, "Artist 2", "iterator->next ok" ); +is( $it->next, undef, "next past end of resultset ok" ); + + +# clean up our mess +END { + $dbh->do('DROP TABLE artist') if $dbh; +} +