From: Rafael Kitover Date: Wed, 7 Apr 2010 00:13:38 +0000 (+0000) Subject: UUID support for SQL Anywhere X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=548d162784058df6a57a524618337060663f9323;p=dbsrgits%2FDBIx-Class-Historic.git UUID support for SQL Anywhere --- diff --git a/lib/DBIx/Class/Storage/DBI/MSSQL.pm b/lib/DBIx/Class/Storage/DBI/MSSQL.pm index 5f17153..5940868 100644 --- a/lib/DBIx/Class/Storage/DBI/MSSQL.pm +++ b/lib/DBIx/Class/Storage/DBI/MSSQL.pm @@ -3,7 +3,7 @@ package DBIx::Class::Storage::DBI::MSSQL; use strict; use warnings; -use base qw/DBIx::Class::Storage::DBI/; +use base qw/DBIx::Class::Storage::DBI::UniqueIdentifier/; use mro 'c3'; use List::Util(); @@ -66,43 +66,12 @@ sub insert_bulk { } } -# support MSSQL GUID column types - sub insert { my $self = shift; my ($source, $to_insert) = @_; my $supplied_col_info = $self->_resolve_column_info($source, [keys %$to_insert] ); - my %guid_cols; - my @pk_cols = $source->primary_columns; - my %pk_cols; - @pk_cols{@pk_cols} = (); - - my @pk_guids = grep { - $source->column_info($_)->{data_type} - && - $source->column_info($_)->{data_type} =~ /^uniqueidentifier/i - } @pk_cols; - - my @auto_guids = grep { - $source->column_info($_)->{data_type} - && - $source->column_info($_)->{data_type} =~ /^uniqueidentifier/i - && - $source->column_info($_)->{auto_nextval} - } grep { not exists $pk_cols{$_} } $source->columns; - - my @get_guids_for = - grep { not exists $to_insert->{$_} } (@pk_guids, @auto_guids); - - my $updated_cols = {}; - - for my $guid_col (@get_guids_for) { - my ($new_guid) = $self->_get_dbh->selectrow_array('SELECT NEWID()'); - $updated_cols->{$guid_col} = $to_insert->{$guid_col} = $new_guid; - } - my $is_identity_insert = (List::Util::first { $_->{is_auto_increment} } (values %$supplied_col_info) ) ? 1 : 0; @@ -111,13 +80,12 @@ sub insert { $self->_set_identity_insert ($source->name); } - $updated_cols = { %$updated_cols, %{ $self->next::method(@_) } }; + my $updated_cols = $self->next::method(@_); if ($is_identity_insert) { $self->_unset_identity_insert ($source->name); } - return $updated_cols; } @@ -363,7 +331,7 @@ different/better way to get the same result - please file a bugreport. =head1 AUTHOR -See L. +See L and L. =head1 LICENSE diff --git a/lib/DBIx/Class/Storage/DBI/SQLAnywhere.pm b/lib/DBIx/Class/Storage/DBI/SQLAnywhere.pm index 73a5df0..bcd47eb 100644 --- a/lib/DBIx/Class/Storage/DBI/SQLAnywhere.pm +++ b/lib/DBIx/Class/Storage/DBI/SQLAnywhere.pm @@ -2,7 +2,7 @@ package DBIx::Class::Storage::DBI::SQLAnywhere; use strict; use warnings; -use base qw/DBIx::Class::Storage::DBI/; +use base qw/DBIx::Class::Storage::DBI::UniqueIdentifier/; use mro 'c3'; use List::Util (); @@ -35,6 +35,8 @@ Recommended L settings: sub last_insert_id { shift->_identity } +sub _new_uuid { 'UUIDTOSTR(NEWID())' } + sub insert { my $self = shift; my ($source, $to_insert) = @_; @@ -46,7 +48,9 @@ sub insert { # user might have an identity PK without is_auto_increment if (not $identity_col) { foreach my $pk_col ($source->primary_columns) { - if (not exists $to_insert->{$pk_col}) { + if (not exists $to_insert->{$pk_col} && + $source->column_info($pk_col)->{data_type} !~ /^uniqueidentifier/i) + { $identity_col = $pk_col; last; } @@ -58,11 +62,41 @@ sub insert { my $table_name = $source->from; $table_name = $$table_name if ref $table_name; - my ($identity) = $dbh->selectrow_array("SELECT GET_IDENTITY('$table_name')"); + my ($identity) = eval { + local $@; $dbh->selectrow_array("SELECT GET_IDENTITY('$table_name')") + }; + + if (defined $identity) { + $to_insert->{$identity_col} = $identity; + $self->_identity($identity); + } + } + + return $self->next::method(@_); +} + +# convert UUIDs to strings in selects +sub _select_args { + my $self = shift; + my ($ident, $select) = @_; + + my ($alias2source, $rs_alias) = $self->_resolve_ident_sources($ident); + + for my $select_idx (0..$#$select) { + my $selected = $select->[$select_idx]; - $to_insert->{$identity_col} = $identity; + next if ref $selected; - $self->_identity($identity); + my ($alias, $col) = split /\./, $selected; + ($alias, $col) = ($rs_alias, $selected) if not defined $col; + + my $data_type = eval { + $alias2source->{$alias}->column_info($col)->{data_type} + }; + + if ($data_type && $data_type =~ /^uniqueidentifier\z/i) { + $select->[$select_idx] = { UUIDTOSTR => $selected }; + } } return $self->next::method(@_); diff --git a/lib/DBIx/Class/Storage/DBI/UniqueIdentifier.pm b/lib/DBIx/Class/Storage/DBI/UniqueIdentifier.pm new file mode 100644 index 0000000..6a70662 --- /dev/null +++ b/lib/DBIx/Class/Storage/DBI/UniqueIdentifier.pm @@ -0,0 +1,83 @@ +package DBIx::Class::Storage::DBI::UniqueIdentifier; + +use strict; +use warnings; +use base 'DBIx::Class::Storage::DBI'; +use mro 'c3'; + +=head1 NAME + +DBIx::Class::Storage::DBI::UniqueIdentifier - Storage component for RDBMSes +supporting the 'uniqueidentifier' type + +=head1 DESCRIPTION + +This is a storage component for databases that support the C +type and the C function for generating UUIDs. + +UUIDs are generated automatically for PK columns with the C +L, as well as non-PK with this +L and +L. + +Currently used by L and +L. + +The composing class can define a C<_new_uuid> method to override the function +used to generate a new UUID. + +=cut + +sub _new_uuid { 'NEWID()' } + +sub insert { + my $self = shift; + my ($source, $to_insert) = @_; + + my $supplied_col_info = $self->_resolve_column_info($source, [keys %$to_insert] ); + + my %guid_cols; + my @pk_cols = $source->primary_columns; + my %pk_cols; + @pk_cols{@pk_cols} = (); + + my @pk_guids = grep { + $source->column_info($_)->{data_type} + && + $source->column_info($_)->{data_type} =~ /^uniqueidentifier/i + } @pk_cols; + + my @auto_guids = grep { + $source->column_info($_)->{data_type} + && + $source->column_info($_)->{data_type} =~ /^uniqueidentifier/i + && + $source->column_info($_)->{auto_nextval} + } grep { not exists $pk_cols{$_} } $source->columns; + + my @get_guids_for = + grep { not exists $to_insert->{$_} } (@pk_guids, @auto_guids); + + my $updated_cols = {}; + + for my $guid_col (@get_guids_for) { + my ($new_guid) = $self->_get_dbh->selectrow_array('SELECT '.$self->_new_uuid); + $updated_cols->{$guid_col} = $to_insert->{$guid_col} = $new_guid; + } + + $updated_cols = { %$updated_cols, %{ $self->next::method(@_) } }; + + return $updated_cols; +} + +=head1 AUTHOR + +See L and L. + +=head1 LICENSE + +You may distribute this code under the same terms as Perl itself. + +=cut + +1; diff --git a/t/749sybase_asa.t b/t/749sybase_asa.t index fe984bc..03c1182 100644 --- a/t/749sybase_asa.t +++ b/t/749sybase_asa.t @@ -3,9 +3,12 @@ use warnings; use Test::More; use Test::Exception; +use Scope::Guard (); use lib qw(t/lib); use DBICTest; +DBICTest::Schema->load_classes('ArtistGUID'); + # tests stolen from 748informix.t my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_SYBASE_ASA_${_}" } qw/DSN USER PASS/}; @@ -21,20 +24,20 @@ my @info = ( [ $dsn2, $user2, $pass2 ], ); -my @handles_to_clean; +my $schema; foreach my $info (@info) { my ($dsn, $user, $pass) = @$info; next unless $dsn; - my $schema = DBICTest::Schema->connect($dsn, $user, $pass, { + $schema = DBICTest::Schema->connect($dsn, $user, $pass, { auto_savepoint => 1 }); - my $dbh = $schema->storage->dbh; + my $guard = Scope::Guard->new(\&cleanup); - push @handles_to_clean, $dbh; + my $dbh = $schema->storage->dbh; eval { $dbh->do("DROP TABLE artist") }; @@ -160,13 +163,62 @@ EOF ok($rs->find($id)->$type eq $binstr{$size}, "verified inserted $size $type" ); } } + + my @uuid_types = qw/uniqueidentifier uniqueidentifierstr/; + +# test uniqueidentifiers + for my $uuid_type (@uuid_types) { + local $schema->source('ArtistGUID')->column_info('artistid')->{data_type} + = $uuid_type; + + local $schema->source('ArtistGUID')->column_info('a_guid')->{data_type} + = $uuid_type; + + $schema->storage->dbh_do (sub { + my ($storage, $dbh) = @_; + eval { $dbh->do("DROP TABLE artist") }; + $dbh->do(<<"SQL"); +CREATE TABLE artist ( + artistid $uuid_type NOT NULL, + name VARCHAR(100), + rank INT NOT NULL DEFAULT '13', + charfield CHAR(10) NULL, + a_guid $uuid_type, + primary key(artistid) +) +SQL + }); + + my $row; + lives_ok { + $row = $schema->resultset('ArtistGUID')->create({ name => 'mtfnpy' }) + } 'created a row with a GUID'; + + ok( + eval { $row->artistid }, + 'row has GUID PK col populated', + ); + diag $@ if $@; + + ok( + eval { $row->a_guid }, + 'row has a GUID col with auto_nextval populated', + ); + diag $@ if $@; + + my $row_from_db = $schema->resultset('ArtistGUID') + ->search({ name => 'mtfnpy' })->first; + + is $row_from_db->artistid, $row->artistid, + 'PK GUID round trip'; + + is $row_from_db->a_guid, $row->a_guid, + 'NON-PK GUID round trip'; + } } done_testing; -# clean up our mess -END { - foreach my $dbh (@handles_to_clean) { - eval { $dbh->do("DROP TABLE $_") } for qw/artist bindtype_test/; - } +sub cleanup { + eval { $schema->storage->dbh->do("DROP TABLE $_") } for qw/artist bindtype_test/; }