Merge branch 'master' into topic/constructor_rewrite
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / UniqueIdentifier.pm
diff --git a/lib/DBIx/Class/Storage/DBI/UniqueIdentifier.pm b/lib/DBIx/Class/Storage/DBI/UniqueIdentifier.pm
new file mode 100644 (file)
index 0000000..8621da0
--- /dev/null
@@ -0,0 +1,121 @@
+package DBIx::Class::Storage::DBI::UniqueIdentifier;
+
+use strict;
+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 GUID types
+
+=head1 DESCRIPTION
+
+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
+
+In which case the coderef should return a string GUID, using L<Data::GUID>, or
+whatever GUID generation method you prefer. It is passed the C<$self>
+L<DBIx::Class::Storage> reference as a parameter.
+
+=back
+
+For example:
+
+  $schema->storage->new_guid(sub { Data::GUID->new->as_string });
+
+=cut
+
+my $GUID_TYPE = qr/^(?:uniqueidentifier(?:str)?|guid)\z/i;
+
+sub _is_guid_type {
+  my ($self, $data_type) = @_;
+
+  return $data_type =~ $GUID_TYPE;
+}
+
+sub _prefetch_autovalues  {
+  my $self = shift;
+  my ($source, $to_insert) = @_;
+
+  my $col_info = $source->columns_info;
+
+  my %guid_cols;
+  my @pk_cols = $source->primary_columns;
+  my %pk_col_idx;
+  @pk_col_idx{@pk_cols} = ();
+
+  my @pk_guids = grep {
+    $col_info->{$_}{data_type}
+    &&
+    $col_info->{$_}{data_type} =~ $GUID_TYPE
+  } @pk_cols;
+
+  my @auto_guids = grep {
+    $col_info->{$_}{data_type}
+    &&
+    $col_info->{$_}{data_type} =~ $GUID_TYPE
+    &&
+    $col_info->{$_}{auto_nextval}
+  } grep { not exists $pk_col_idx{$_} } $source->columns;
+
+  my @get_guids_for =
+    grep { not exists $to_insert->{$_} } (@pk_guids, @auto_guids);
+
+  for my $guid_col (@get_guids_for) {
+    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') {
+      $to_insert->{$guid_col} = $guid_method->($self);
+    }
+    else {
+      ($to_insert->{$guid_col}) = $self->_get_dbh->selectrow_array("SELECT $guid_method");
+    }
+  }
+
+  return $self->next::method(@_);
+}
+
+=head1 AUTHOR
+
+See L<DBIx::Class/AUTHOR> and L<DBIx::Class/CONTRIBUTORS>.
+
+=head1 LICENSE
+
+You may distribute this code under the same terms as Perl itself.
+
+=cut
+
+1;