Refactor UUID generation logic in ::Storage::DBI::UniqueIdentifier
Rafael Kitover [Fri, 4 Feb 2011 14:42:31 +0000 (09:42 -0500)]
lib/DBIx/Class/Storage/DBI/MSSQL.pm
lib/DBIx/Class/Storage/DBI/SQLAnywhere.pm
lib/DBIx/Class/Storage/DBI/UniqueIdentifier.pm

index 8f5b528..d4f87cc 100644 (file)
@@ -17,6 +17,8 @@ __PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker::MSSQL');
 
 __PACKAGE__->sql_quote_char([qw/[ ]/]);
 
+__PACKAGE__->new_guid('NEWID()');
+
 sub _set_identity_insert {
   my ($self, $table) = @_;
 
index 38fd196..3705b9e 100644 (file)
@@ -12,6 +12,8 @@ __PACKAGE__->mk_group_accessors(simple => qw/_identity/);
 __PACKAGE__->sql_limit_dialect ('RowNumberOver');
 __PACKAGE__->sql_quote_char ('"');
 
+__PACKAGE__->new_guid('UUIDTOSTR(NEWID())');
+
 =head1 NAME
 
 DBIx::Class::Storage::DBI::SQLAnywhere - Driver for Sybase SQL Anywhere
@@ -36,8 +38,6 @@ Recommended L<connect_info|DBIx::Class::Storage::DBI/connect_info> settings:
 
 sub last_insert_id { shift->_identity }
 
-sub _new_uuid { 'UUIDTOSTR(NEWID())' }
-
 sub _prefetch_autovalues {
   my $self = shift;
   my ($source, $to_insert) = @_;
index a748994..92e7c15 100644 (file)
@@ -5,30 +5,56 @@ use warnings;
 use base 'DBIx::Class::Storage::DBI';
 use mro 'c3';
 
+__PACKAGE__->mk_group_accessors(inherited => 'new_guid');
+
 =head1 NAME
 
 DBIx::Class::Storage::DBI::UniqueIdentifier - Storage component for RDBMSes
-supporting the 'uniqueidentifier' type
+supporting GUID types
 
 =head1 DESCRIPTION
 
-This is a storage component for databases that support the C<uniqueidentifier>
-type and the C<NEWID()> function for generating UUIDs.
+This is a storage component for databases that support GUID types such as
+C<uniqueidentifier>, C<uniqueidentifierstr> or C<guid>.
+
+GUIDs are generated automatically for PK columns with a supported
+L<data_type|DBIx::Class::ResultSource/data_type>, as well as non-PK with
+L<auto_nextval|DBIx::Class::ResultSource/auto_nextval> set.
+
+=head1 METHODS
+
+=head2 new_guid
+
+The composing class must set C<new_guid> to the method used to generate a new
+GUID. It can also set it to C<undef>, in which case the user is required to set
+it, or a runtime error will be thrown. It can be:
+
+=over 4
+
+=item string
+
+In which case it is used as the name of database function to create a new GUID,
+
+=item coderef
 
-UUIDs are generated automatically for PK columns with the C<uniqueidentifier>
-L<data_type|DBIx::Class::ResultSource/data_type>, as well as non-PK with this
-L<data_type|DBIx::Class::ResultSource/data_type> and
-L<auto_nextval|DBIx::Class::ResultSource/auto_nextval>.
+In which case the coderef should return a string GUID, using L<Data::GUID>, or
+whatever GUID generation method you prefer.
 
-Currently used by L<DBIx::Class::Storage::DBI::MSSQL> and
-L<DBIx::Class::Storage::DBI::SQLAnywhere>.
+=back
 
-The composing class can define a C<_new_uuid> method to override the function
-used to generate a new UUID.
+For example:
+
+  $schema->storage->new_guid(sub { Data::GUID->new->as_string });
 
 =cut
 
-sub _new_uuid { 'NEWID()' }
+my $GUID_TYPE = qr/^(?:uniqueidentifier(?:str)?|guid)\z/i;
+
+sub _is_guid_type {
+  my ($self, $data_type) = @_;
+
+  return $data_type =~ $GUID_TYPE;
+}
 
 sub insert {
   my $self = shift;
@@ -44,13 +70,13 @@ sub insert {
   my @pk_guids = grep {
     $col_info->{$_}{data_type}
     &&
-    $col_info->{$_}{data_type} =~ /^uniqueidentifier/i
+    $col_info->{$_}{data_type} =~ $GUID_TYPE
   } @pk_cols;
 
   my @auto_guids = grep {
     $col_info->{$_}{data_type}
     &&
-    $col_info->{$_}{data_type} =~ /^uniqueidentifier/i
+    $col_info->{$_}{data_type} =~ $GUID_TYPE
     &&
     $col_info->{$_}{auto_nextval}
   } grep { not exists $pk_cols{$_} } $source->columns;
@@ -61,7 +87,24 @@ sub insert {
   my $updated_cols = {};
 
   for my $guid_col (@get_guids_for) {
-    my ($new_guid) = $self->_get_dbh->selectrow_array('SELECT '.$self->_new_uuid);
+    my $new_guid;
+
+    my $guid_method = $self->new_guid;
+
+    if (not defined $guid_method) {
+      $self->throw_exception(
+        'You must set new_guid on your storage. See perldoc '
+       .'DBIx::Class::Storage::DBI::UniqueIdentifier'
+      );
+    }
+
+    if (ref $guid_method eq 'CODE') {
+      $new_guid = $guid_method->();
+    }
+    else {
+      ($new_guid) = $self->_get_dbh->selectrow_array("SELECT $guid_method");
+    }
+
     $updated_cols->{$guid_col} = $to_insert->{$guid_col} = $new_guid;
   }