From: Jess Robinson Date: Tue, 3 Jun 2008 20:26:30 +0000 (+0000) Subject: Applied patch from Ryan to uniqify index names sanely for the mysql producer X-Git-Tag: v0.11008~312 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=f5405d4730be14a754d64a928d85695148a2e55c;p=dbsrgits%2FSQL-Translator.git Applied patch from Ryan to uniqify index names sanely for the mysql producer --- diff --git a/Build.PL b/Build.PL index 5102d04..c3f85d6 100644 --- a/Build.PL +++ b/Build.PL @@ -24,6 +24,7 @@ my $builder = Module::Build->new( 'Class::Base' => 0, 'Class::Data::Inheritable' => 0.02, 'Class::MakeMethods' => 0, + 'Digest::SHA1' => 2.00, 'IO::Dir' => 0, 'Log::Log4perl' => 0, 'Parse::RecDescent' => 1.94, diff --git a/Changes b/Changes index ca1afd0..e8741f4 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,7 @@ * Added support for proper booleans in the mysql producer, when a mysql version of at least 4.x is supplied * Added support for proper enums under pg (as of 8.3), with pg version check, and deferrable constraints +* Added support to truncate long constraint and index names in the mysql producer, because of a change to DBIx::Class to produce such long names in some cases. # ---------------------------------------------------------- # 0.0900 2008-02-25 diff --git a/lib/SQL/Translator.pm b/lib/SQL/Translator.pm index 4e34d0a..a4b6933 100644 --- a/lib/SQL/Translator.pm +++ b/lib/SQL/Translator.pm @@ -1306,6 +1306,8 @@ The following people have contributed to the SQLFairy project: =item * Daniel Ruoso +=item * Ryan D Johnson + =back If you would like to contribute to the project, you can send patches diff --git a/lib/SQL/Translator/Producer/MySQL.pm b/lib/SQL/Translator/Producer/MySQL.pm index e3cbd3f..307ef6a 100644 --- a/lib/SQL/Translator/Producer/MySQL.pm +++ b/lib/SQL/Translator/Producer/MySQL.pm @@ -96,9 +96,14 @@ use vars qw[ $VERSION $DEBUG %used_names ]; $VERSION = sprintf "%d.%02d", q$Revision: 1.54 $ =~ /(\d+)\.(\d+)/; $DEBUG = 0 unless defined $DEBUG; +# Maximum length for most identifiers is 64, according to: +# http://dev.mysql.com/doc/refman/4.1/en/identifiers.html +# http://dev.mysql.com/doc/refman/5.0/en/identifiers.html +my $DEFAULT_MAX_ID_LENGTH = 64; + use Data::Dumper; use SQL::Translator::Schema::Constants; -use SQL::Translator::Utils qw(debug header_comment); +use SQL::Translator::Utils qw(debug header_comment truncate_id_uniquely); # # Use only lowercase for the keys (e.g. "long" and not "LONG") @@ -248,6 +253,7 @@ sub produce { my $show_warnings = $translator->show_warnings || 0; my $producer_args = $translator->producer_args; my $mysql_version = $producer_args->{mysql_version} || 0; + my $max_id_length = $producer_args->{mysql_max_id_length} || $DEFAULT_MAX_ID_LENGTH; my ($qt, $qf, $qc) = ('','', ''); $qt = '`' if $translator->quote_table_names; @@ -275,6 +281,7 @@ sub produce { no_comments => $no_comments, quote_table_names => $qt, quote_field_names => $qf, + max_id_length => $max_id_length, mysql_version => $mysql_version }); } @@ -517,7 +524,7 @@ sub create_index return join( ' ', lc $index->type eq 'normal' ? 'INDEX' : $index->type . ' INDEX', - $index->name, + truncate_id_uniquely( $index->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH ), '(' . $qf . join( "$qf, $qf", $index->fields ) . $qf . ')' ); @@ -583,7 +590,7 @@ sub create_constraint elsif ( $c->type eq UNIQUE ) { return 'UNIQUE '. - (defined $c->name ? $qf.$c->name.$qf.' ' : ''). + (defined $c->name ? $qf.truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH ).$qf.' ' : ''). '(' . $qf . join("$qf, $qf", @fields). $qf . ')'; } elsif ( $c->type eq FOREIGN_KEY ) { @@ -592,7 +599,7 @@ sub create_constraint # my $table = $c->table; - my $c_name = $c->name; + my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH ); my $def = join(' ', map { $_ || () } diff --git a/lib/SQL/Translator/Utils.pm b/lib/SQL/Translator/Utils.pm index cefa284..e85000a 100644 --- a/lib/SQL/Translator/Utils.pm +++ b/lib/SQL/Translator/Utils.pm @@ -24,12 +24,14 @@ use strict; use base qw(Exporter); use vars qw($VERSION $DEFAULT_COMMENT @EXPORT_OK); +use Digest::SHA1 qw( sha1_hex ); + use Exporter; $VERSION = sprintf "%d.%02d", q$Revision: 1.12 $ =~ /(\d+)\.(\d+)/; $DEFAULT_COMMENT = '-- '; @EXPORT_OK = qw( - debug normalize_name header_comment parse_list_arg $DEFAULT_COMMENT + debug normalize_name header_comment parse_list_arg truncate_id_uniquely $DEFAULT_COMMENT ); # ---------------------------------------------------------------------- @@ -143,6 +145,32 @@ sub parse_list_arg { } } +# ---------------------------------------------------------------------- +# truncate_id_uniquely( $desired_name, $max_symbol_length ) +# +# Truncates the name $desired_name to the $max_symbol_length by +# including part of the hash of the full name at the end of the +# truncated name, giving a high probability that the symbol will be +# unique. +# ---------------------------------------------------------------------- +my $COLLISION_TAG_LENGTH = 8; +sub truncate_id_uniquely { + my ( $desired_name, $max_symbol_length ) = @_; + + return $desired_name unless defined $desired_name && length $desired_name > $max_symbol_length; + + my $truncated_name = substr $desired_name, 0, $max_symbol_length - $COLLISION_TAG_LENGTH - 1; + + # Hex isn't the most space-efficient, but it skirts around allowed + # charset issues + my $digest = sha1_hex($desired_name); + my $collision_tag = substr $digest, 0, $COLLISION_TAG_LENGTH; + + return $truncated_name + . '_' + . $collision_tag; +} + 1; # ---------------------------------------------------------------------- @@ -250,6 +278,23 @@ All of the following will return equivalent values: parse_list_arg( [ 'id', 'name' ] ); parse_list_arg( qw[ id name ] ); +=head2 truncate_id_uniquely + +Takes a string ($desired_name) and int ($max_symbol_length). Truncates +$desired_name to $max_symbol_length by including part of the hash of +the full name at the end of the truncated name, giving a high +probability that the symbol will be unique. For example, + + truncate_id_uniquely( 'a' x 100, 64 ) + truncate_id_uniquely( 'a' x 99 . 'b', 64 ); + truncate_id_uniquely( 'a' x 99, 64 ) + +Will give three different results; specifically: + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_7f900025 + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_6191e39a + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_8cd96af2 + =head2 $DEFAULT_COMMENT This is the default comment string, '-- ' by default. Useful for diff --git a/t/38-mysql-producer.t b/t/38-mysql-producer.t index ce5bbdd..b909e93 100644 --- a/t/38-mysql-producer.t +++ b/t/38-mysql-producer.t @@ -102,7 +102,7 @@ schema: - type: NORMAL fields: - id - name: index_2 + name: really_long_name_bigger_than_64_chars_aaaaaaaaaaaaaaaaaaaaaaaaaaa constraints: - type: PRIMARY_KEY fields: @@ -126,8 +126,8 @@ my @stmts = ( "CREATE TABLE `thing` ( `id` unsigned int auto_increment, `name` varchar(32), - `swedish_name` varchar(32) CHARACTER SET swe7, - `description` text CHARACTER SET utf8 COLLATE utf8_general_ci, + `swedish_name` varchar(32) character set swe7, + `description` text character set utf8 collate utf8_general_ci, PRIMARY KEY (`id`), UNIQUE `idx_unique_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARACTER SET latin1 COLLATE latin1_danish_ci;\n\n", @@ -138,7 +138,7 @@ my @stmts = ( `foo` integer, `foo2` integer, INDEX index_1 (`id`), - INDEX index_2 (`id`), + INDEX really_long_name_bigger_than_64_chars_aaaaaaaaaaaaaaaaa_aed44c47 (`id`), INDEX (`foo`), INDEX (`foo2`), PRIMARY KEY (`id`, `foo`),