1 package SQL::Translator::Producer::MySQL;
5 SQL::Translator::Producer::MySQL - MySQL-specific producer for SQL::Translator
9 Use via SQL::Translator:
13 my $t = SQL::Translator->new( parser => '...', producer => 'MySQL', '...' );
18 This module will produce text output of the schema suitable for MySQL.
19 There are still some issues to be worked out with syntax differences
20 between MySQL versions 3 and 4 ("SET foreign_key_checks," character sets
25 This producer takes a single optional producer_arg C<mysql_version>, which
26 provides the desired version for the target database. By default MySQL v3 is
27 assumed, and statements pertaining to any features introduced in later versions
28 (e.g. CREATE VIEW) are not produced.
30 Valid version specifiers for C<mysql_version> are listed L<here|SQL::Translator::Utils/parse_mysql_version>
34 Normally the tables will be created without any explicit table type given and
35 so will use the MySQL default.
37 Any tables involved in foreign key constraints automatically get a table type
38 of InnoDB, unless this is overridden by setting the C<mysql_table_type> extra
39 attribute explicitly on the table.
41 =head2 Extra attributes.
43 The producer recognises the following extra attributes on the Schema objects.
49 Set the list of allowed values for Enum fields.
51 =item B<field.binary>, B<field.unsigned>, B<field.zerofill>
53 Set the MySQL field options of the same name.
55 =item B<field.renamed_from>, B<table.renamed_from>
57 Use when producing diffs to indicate that the current table/field has been
58 renamed from the old name as given in the attribute value.
60 =item B<table.mysql_table_type>
62 Set the type of the table e.g. 'InnoDB', 'MyISAM'. This will be
63 automatically set for tables involved in foreign key constraints if it is
64 not already set explicitly. See L<"Table Types">.
66 Please note that the C<ENGINE> option is the preferred method of specifying
67 the MySQL storage engine to use, but this method still works for backwards
70 =item B<table.mysql_charset>, B<table.mysql_collate>
72 Set the tables default charater set and collation order.
74 =item B<field.mysql_charset>, B<field.mysql_collate>
76 Set the fields charater set and collation order.
84 our ( $DEBUG, %used_names );
85 our $VERSION = '1.59';
86 $DEBUG = 0 unless defined $DEBUG;
88 # Maximum length for most identifiers is 64, according to:
89 # http://dev.mysql.com/doc/refman/4.1/en/identifiers.html
90 # http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
91 my $DEFAULT_MAX_ID_LENGTH = 64;
94 use SQL::Translator::Schema::Constants;
95 use SQL::Translator::Utils qw(debug header_comment
96 truncate_id_uniquely parse_mysql_version);
99 # Use only lowercase for the keys (e.g. "long" and not "LONG")
105 varchar2 => 'varchar',
121 'long integer' => 'integer',
123 'datetime' => 'datetime',
132 # Column types that do not support lenth attribute
134 my @no_length_attr = qw/
135 date time timestamp datetime year
139 sub preprocess_schema {
142 # extra->{mysql_table_type} used to be the type. It belongs in options, so
143 # move it if we find it. Return Engine type if found in extra or options
144 # Similarly for mysql_charset and mysql_collate
145 my $extra_to_options = sub {
146 my ($table, $extra_name, $opt_name) = @_;
148 my $extra = $table->extra;
150 my $extra_type = delete $extra->{$extra_name};
152 # Now just to find if there is already an Engine or Type option...
153 # and lets normalize it to ENGINE since:
155 # The ENGINE table option specifies the storage engine for the table.
156 # TYPE is a synonym, but ENGINE is the preferred option name.
159 # We have to use the hash directly here since otherwise there is no way
161 my $options = ( $table->{options} ||= []);
163 # If multiple option names, normalize to the first one
165 OPT_NAME: for ( @$opt_name[1..$#$opt_name] ) {
166 for my $idx ( 0..$#{$options} ) {
167 my ($key, $value) = %{ $options->[$idx] };
170 $options->[$idx] = { $opt_name->[0] => $value };
175 $opt_name = $opt_name->[0];
180 # This assumes that there isn't both a Type and an Engine option.
182 for my $idx ( 0..$#{$options} ) {
183 my ($key, $value) = %{ $options->[$idx] };
185 next unless uc $key eq $opt_name;
187 # make sure case is right on option name
188 delete $options->[$idx]{$key};
189 return $options->[$idx]{$opt_name} = $value || $extra_type;
194 push @$options, { $opt_name => $extra_type };
200 # Names are only specific to a given schema
201 local %used_names = ();
204 # Work out which tables need to be InnoDB to support foreign key
205 # constraints. We do this first as we need InnoDB at both ends.
207 foreach my $table ( $schema->get_tables ) {
209 $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE'] );
210 $extra_to_options->($table, 'mysql_charset', 'CHARACTER SET' );
211 $extra_to_options->($table, 'mysql_collate', 'COLLATE' );
213 foreach my $c ( $table->get_constraints ) {
214 next unless $c->type eq FOREIGN_KEY;
216 # Normalize constraint names here.
217 my $c_name = $c->name;
218 # Give the constraint a name if it doesn't have one, so it doens't feel
220 $c_name = $table->name . '_fk' unless length $c_name;
222 $c->name( next_unused_name($c_name) );
224 for my $meth (qw/table reference_table/) {
225 my $table = $schema->get_table($c->$meth) || next;
226 # This normalizes the types to ENGINE and returns the value if its there
227 next if $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE']);
228 $table->options( { 'ENGINE' => 'InnoDB' } );
230 } # foreach constraints
232 my %map = ( mysql_collate => 'collate', mysql_charset => 'character set');
233 foreach my $f ( $table->get_fields ) {
234 my $extra = $f->extra;
236 $extra->{$map{$_}} = delete $extra->{$_} if exists $extra->{$_};
240 if ( !$size[0] && $f->data_type =~ /char$/ ) {
249 my $translator = shift;
250 local $DEBUG = $translator->debug;
252 my $no_comments = $translator->no_comments;
253 my $add_drop_table = $translator->add_drop_table;
254 my $schema = $translator->schema;
255 my $show_warnings = $translator->show_warnings || 0;
256 my $producer_args = $translator->producer_args;
257 my $mysql_version = parse_mysql_version ($producer_args->{mysql_version}, 'perl') || 0;
258 my $max_id_length = $producer_args->{mysql_max_id_length} || $DEFAULT_MAX_ID_LENGTH;
260 my ($qt, $qf, $qc) = ('','', '');
261 $qt = '`' if $translator->quote_table_names;
262 $qf = '`' if $translator->quote_field_names;
264 debug("PKG: Beginning production\n");
267 $create .= header_comment unless ($no_comments);
268 # \todo Don't set if MySQL 3.x is set on command line
269 my @create = "SET foreign_key_checks=0";
271 preprocess_schema($schema);
278 for my $table ( $schema->get_tables ) {
279 # print $table->name, "\n";
280 push @table_defs, create_table($table,
281 { add_drop_table => $add_drop_table,
282 show_warnings => $show_warnings,
283 no_comments => $no_comments,
284 quote_table_names => $qt,
285 quote_field_names => $qf,
286 max_id_length => $max_id_length,
287 mysql_version => $mysql_version
291 if ($mysql_version >= 5.000001) {
292 for my $view ( $schema->get_views ) {
293 push @table_defs, create_view($view,
294 { add_replace_view => $add_drop_table,
295 show_warnings => $show_warnings,
296 no_comments => $no_comments,
297 quote_table_names => $qt,
298 quote_field_names => $qf,
299 max_id_length => $max_id_length,
300 mysql_version => $mysql_version
305 if ($mysql_version >= 5.000002) {
306 for my $trigger ( $schema->get_triggers ) {
307 push @table_defs, create_trigger($trigger,
308 { add_drop_trigger => $add_drop_table,
309 show_warnings => $show_warnings,
310 no_comments => $no_comments,
311 quote_table_names => $qt,
312 quote_field_names => $qf,
313 max_id_length => $max_id_length,
314 mysql_version => $mysql_version
320 # print "@table_defs\n";
321 push @table_defs, "SET foreign_key_checks=1";
323 return wantarray ? ($create ? $create : (), @create, @table_defs) : ($create . join('', map { $_ ? "$_;\n\n" : () } (@create, @table_defs)));
327 my ($trigger, $options) = @_;
328 my $qt = $options->{quote_table_names} || '';
329 my $qf = $options->{quote_field_names} || '';
331 my $trigger_name = $trigger->name;
332 debug("PKG: Looking at trigger '${trigger_name}'\n");
336 my $events = $trigger->database_events;
337 for my $event ( @$events ) {
338 my $name = $trigger_name;
342 warn "Multiple database events supplied for trigger '${trigger_name}', ",
343 "creating trigger '${name}' for the '${event}' event\n"
344 if $options->{show_warnings};
347 my $action = $trigger->action;
348 $action .= ";" unless $action =~ /;\s*\z/;
350 push @statements, "DROP TRIGGER IF EXISTS ${qt}${name}${qt}" if $options->{add_drop_trigger};
351 push @statements, sprintf(
352 "CREATE TRIGGER ${qt}%s${qt} %s %s ON ${qt}%s${qt}\n FOR EACH ROW BEGIN %s END",
353 $name, $trigger->perform_action_when, $event, $trigger->on_table, $action,
357 # Tack the comment onto the first statement
358 $statements[0] = "--\n-- Trigger ${qt}${trigger_name}${qt}\n--\n" . $statements[0] unless $options->{no_comments};
363 my ($view, $options) = @_;
364 my $qt = $options->{quote_table_names} || '';
365 my $qf = $options->{quote_field_names} || '';
367 my $view_name = $view->name;
368 debug("PKG: Looking at view '${view_name}'\n");
370 # Header. Should this look like what mysqldump produces?
372 $create .= "--\n-- View: ${qt}${view_name}${qt}\n--\n" unless $options->{no_comments};
374 $create .= ' OR REPLACE' if $options->{add_replace_view};
377 my $extra = $view->extra;
379 if( exists($extra->{mysql_algorithm}) && defined(my $algorithm = $extra->{mysql_algorithm}) ){
380 $create .= " ALGORITHM = ${algorithm}\n" if $algorithm =~ /(?:UNDEFINED|MERGE|TEMPTABLE)/i;
383 if( exists($extra->{mysql_definer}) && defined(my $user = $extra->{mysql_definer}) ){
384 $create .= " DEFINER = ${user}\n";
387 if( exists($extra->{mysql_security}) && defined(my $security = $extra->{mysql_security}) ){
388 $create .= " SQL SECURITY ${security}\n" if $security =~ /(?:DEFINER|INVOKER)/i;
392 $create .= " VIEW ${qt}${view_name}${qt}";
394 if( my @fields = $view->fields ){
395 my $list = join ', ', map { "${qf}${_}${qf}"} @fields;
396 $create .= " ( ${list} )";
398 if( my $sql = $view->sql ){
399 # do not wrap parenthesis around the selector, mysql doesn't like this
400 # http://bugs.mysql.com/bug.php?id=9198
401 $create .= " AS\n ${sql}\n";
409 my ($table, $options) = @_;
411 my $qt = $options->{quote_table_names} || '';
412 my $qf = $options->{quote_field_names} || '';
414 my $table_name = quote_table_name($table->name, $qt);
415 debug("PKG: Looking at table '$table_name'\n");
418 # Header. Should this look like what mysqldump produces?
422 $create .= "--\n-- Table: $table_name\n--\n" unless $options->{no_comments};
423 $drop = qq[DROP TABLE IF EXISTS $table_name] if $options->{add_drop_table};
424 $create .= "CREATE TABLE $table_name (\n";
430 for my $field ( $table->get_fields ) {
431 push @field_defs, create_field($field, $options);
439 for my $index ( $table->get_indices ) {
440 push @index_defs, create_index($index, $options);
441 $indexed_fields{ $_ } = 1 for $index->fields;
445 # Constraints -- need to handle more than just FK. -ky
448 my @constraints = $table->get_constraints;
449 for my $c ( @constraints ) {
450 my $constr = create_constraint($c, $options);
451 push @constraint_defs, $constr if($constr);
453 unless ( $indexed_fields{ ($c->fields())[0] } || $c->type ne FOREIGN_KEY ) {
454 push @index_defs, "INDEX ($qf" . ($c->fields())[0] . "$qf)";
455 $indexed_fields{ ($c->fields())[0] } = 1;
459 $create .= join(",\n", map { " $_" }
460 @field_defs, @index_defs, @constraint_defs
467 $create .= generate_table_options($table, $options) || '';
468 # $create .= ";\n\n";
470 return $drop ? ($drop,$create) : $create;
473 sub quote_table_name {
474 my ($table_name, $qt) = @_;
476 $table_name =~ s/\./$qt.$qt/g;
478 return "$qt$table_name$qt";
481 sub generate_table_options
483 my ($table, $options) = @_;
486 my $table_type_defined = 0;
487 my $qf = $options->{quote_field_names} ||= '';
488 my $charset = $table->extra('mysql_charset');
489 my $collate = $table->extra('mysql_collate');
491 for my $t1_option_ref ( $table->options ) {
492 my($key, $value) = %{$t1_option_ref};
493 $table_type_defined = 1
494 if uc $key eq 'ENGINE' or uc $key eq 'TYPE';
495 if (uc $key eq 'CHARACTER SET') {
498 } elsif (uc $key eq 'COLLATE') {
501 } elsif (uc $key eq 'UNION') {
502 $union = "($qf". join("$qf, $qf", @$value) ."$qf)";
505 $create .= " $key=$value";
508 my $mysql_table_type = $table->extra('mysql_table_type');
509 $create .= " ENGINE=$mysql_table_type"
510 if $mysql_table_type && !$table_type_defined;
511 my $comments = $table->comments;
513 $create .= " DEFAULT CHARACTER SET $charset" if $charset;
514 $create .= " COLLATE $collate" if $collate;
515 $create .= " UNION=$union" if $union;
516 $create .= qq[ comment='$comments'] if $comments;
522 my ($field, $options) = @_;
524 my $qf = $options->{quote_field_names} ||= '';
526 my $field_name = $field->name;
527 debug("PKG: Looking at field '$field_name'\n");
528 my $field_def = "$qf$field_name$qf";
531 my $data_type = $field->data_type;
532 my @size = $field->size;
533 my %extra = $field->extra;
534 my $list = $extra{'list'} || [];
535 # \todo deal with embedded quotes
536 my $commalist = join( ', ', map { qq['$_'] } @$list );
537 my $charset = $extra{'mysql_charset'};
538 my $collate = $extra{'mysql_collate'};
540 my $mysql_version = $options->{mysql_version} || 0;
542 # Oracle "number" type -- figure best MySQL type
544 if ( lc $data_type eq 'number' ) {
546 if ( scalar @size > 1 ) {
547 $data_type = 'double';
549 elsif ( $size[0] && $size[0] >= 12 ) {
550 $data_type = 'bigint';
552 elsif ( $size[0] && $size[0] <= 1 ) {
553 $data_type = 'tinyint';
560 # Convert a large Oracle varchar to "text"
561 # (not necessary as of 5.0.3 http://dev.mysql.com/doc/refman/5.0/en/char.html)
563 elsif ( $data_type =~ /char/i && $size[0] > 255 ) {
564 unless ($size[0] <= 65535 && $mysql_version >= 5.000003 ) {
569 elsif ( $data_type =~ /boolean/i ) {
570 if ($mysql_version >= 4) {
571 $data_type = 'boolean';
574 $commalist = "'0','1'";
577 elsif ( exists $translate{ lc $data_type } ) {
578 $data_type = $translate{ lc $data_type };
581 @size = () if $data_type =~ /(text|blob)/i;
583 if ( $data_type =~ /(double|float)/ && scalar @size == 1 ) {
587 $field_def .= " $data_type";
589 if ( lc($data_type) eq 'enum' || lc($data_type) eq 'set') {
590 $field_def .= '(' . $commalist . ')';
593 defined $size[0] && $size[0] > 0
595 ! grep lc($data_type) eq $_, @no_length_attr
597 $field_def .= '(' . join( ', ', @size ) . ')';
601 $field_def .= " CHARACTER SET $charset" if $charset;
602 $field_def .= " COLLATE $collate" if $collate;
605 for my $qual ( qw[ binary unsigned zerofill ] ) {
606 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
607 $field_def .= " $qual";
609 for my $qual ( 'character set', 'collate', 'on update' ) {
610 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
611 $field_def .= " $qual $val";
615 if ( $field->is_nullable ) {
616 $field_def .= ' NULL';
619 $field_def .= ' NOT NULL';
623 SQL::Translator::Producer->_apply_default_value(
631 if ( my $comments = $field->comments ) {
632 $field_def .= qq[ comment '$comments'];
636 $field_def .= " auto_increment" if $field->is_auto_increment;
641 sub alter_create_index
643 my ($index, $options) = @_;
645 my $qt = $options->{quote_table_names} || '';
646 my $qf = $options->{quote_field_names} || '';
647 my $table_name = quote_table_name($index->table->name, $qt);
658 my ( $index, $options ) = @_;
660 my $qf = $options->{quote_field_names} || '';
665 lc $index->type eq 'normal' ? 'INDEX' : $index->type . ' INDEX',
667 ? $qf . truncate_id_uniquely(
669 $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH
672 '(' . $qf . join( "$qf, $qf", $index->fields ) . $qf . ')'
678 my ($index, $options) = @_;
680 my $qt = $options->{quote_table_names} || '';
681 my $qf = $options->{quote_field_names} || '';
682 my $table_name = quote_table_name($index->table->name, $qt);
689 $index->name || $index->fields
694 sub alter_drop_constraint
696 my ($c, $options) = @_;
698 my $qt = $options->{quote_table_names} || '';
699 my $qc = $options->{quote_field_names} || '';
700 my $table_name = quote_table_name($c->table->name, $qt);
702 my @out = ('ALTER','TABLE',$table_name,'DROP');
703 if($c->type eq PRIMARY_KEY) {
707 push @out, ($c->type eq FOREIGN_KEY ? $c->type : "INDEX"),
708 $qc . $c->name . $qc;
710 return join(' ',@out);
713 sub alter_create_constraint
715 my ($index, $options) = @_;
717 my $qt = $options->{quote_table_names} || '';
718 my $table_name = quote_table_name($index->table->name, $qt);
723 create_constraint(@_) );
726 sub create_constraint
728 my ($c, $options) = @_;
730 my $qf = $options->{quote_field_names} || '';
731 my $qt = $options->{quote_table_names} || '';
732 my $leave_name = $options->{leave_name} || undef;
734 my $reference_table_name = quote_table_name($c->reference_table, $qt);
736 my @fields = $c->fields or next;
738 if ( $c->type eq PRIMARY_KEY ) {
739 return 'PRIMARY KEY (' . $qf . join("$qf, $qf", @fields). $qf . ')';
741 elsif ( $c->type eq UNIQUE ) {
742 return sprintf 'UNIQUE %s(%s)',
743 ((defined $c->name && $c->name)
746 truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH ),
752 ( join ', ', map { "${qf}${_}${qf}" } @fields ),
755 elsif ( $c->type eq FOREIGN_KEY ) {
757 # Make sure FK field is indexed or MySQL complains.
760 my $table = $c->table;
761 my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH );
771 $def .= ' ('.$qf . join( "$qf, $qf", @fields ) . $qf . ')';
773 $def .= ' REFERENCES ' . $reference_table_name;
775 my @rfields = map { $_ || () } $c->reference_fields;
776 unless ( @rfields ) {
777 my $rtable_name = $c->reference_table;
778 if ( my $ref_table = $table->schema->get_table( $rtable_name ) ) {
779 push @rfields, $ref_table->primary_key;
782 warn "Can't find reference table '$rtable_name' " .
783 "in schema\n" if $options->{show_warnings};
788 $def .= ' (' . $qf . join( "$qf, $qf", @rfields ) . $qf . ')';
791 warn "FK constraint on " . $table->name . '.' .
792 join('', @fields) . " has no reference fields\n"
793 if $options->{show_warnings};
796 if ( $c->match_type ) {
798 ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
801 if ( $c->on_delete ) {
802 $def .= ' ON DELETE '. $c->on_delete;
805 if ( $c->on_update ) {
806 $def .= ' ON UPDATE '. $c->on_update;
816 my ($to_table, $options) = @_;
818 my $qt = $options->{quote_table_names} || '';
820 my $table_options = generate_table_options($to_table, $options) || '';
821 my $table_name = quote_table_name($to_table->name, $qt);
822 my $out = sprintf('ALTER TABLE %s%s',
829 sub rename_field { alter_field(@_) }
832 my ($from_field, $to_field, $options) = @_;
834 my $qf = $options->{quote_field_names} || '';
835 my $qt = $options->{quote_table_names} || '';
836 my $table_name = quote_table_name($to_field->table->name, $qt);
838 my $out = sprintf('ALTER TABLE %s CHANGE COLUMN %s %s',
840 $qf . $from_field->name . $qf,
841 create_field($to_field, $options));
848 my ($new_field, $options) = @_;
850 my $qt = $options->{quote_table_names} || '';
851 my $table_name = quote_table_name($new_field->table->name, $qt);
853 my $out = sprintf('ALTER TABLE %s ADD COLUMN %s',
855 create_field($new_field, $options));
863 my ($old_field, $options) = @_;
865 my $qf = $options->{quote_field_names} || '';
866 my $qt = $options->{quote_table_names} || '';
867 my $table_name = quote_table_name($old_field->table->name, $qt);
869 my $out = sprintf('ALTER TABLE %s DROP COLUMN %s',
871 $qf . $old_field->name . $qf);
877 sub batch_alter_table {
878 my ($table, $diff_hash, $options) = @_;
880 # InnoDB has an issue with dropping and re-adding a FK constraint under the
881 # name in a single alter statment, see: http://bugs.mysql.com/bug.php?id=13741
883 # We have to work round this.
886 my %fks_to_drop = map {
887 $_->type eq FOREIGN_KEY
890 } @{$diff_hash->{alter_drop_constraint} };
892 my %fks_to_create = map {
893 if ( $_->type eq FOREIGN_KEY) {
894 $fks_to_alter{$_->name} = $fks_to_drop{$_->name} if $fks_to_drop{$_->name};
897 } @{$diff_hash->{alter_create_constraint} };
900 if (scalar keys %fks_to_alter) {
901 $diff_hash->{alter_drop_constraint} = [
902 grep { !$fks_to_alter{$_->name} } @{ $diff_hash->{alter_drop_constraint} }
905 @drop_stmt = batch_alter_table($table, { alter_drop_constraint => [ values %fks_to_alter ] }, $options);
910 if (@{ $diff_hash->{$_} || [] }) {
911 my $meth = __PACKAGE__->can($_) or die __PACKAGE__ . " cant $_";
912 map { $meth->( (ref $_ eq 'ARRAY' ? @$_ : $_), $options ) } @{ $diff_hash->{$_} }
915 alter_drop_constraint
922 alter_create_constraint
926 my $qt = $options->{quote_table_names} || '';
928 # rename_table makes things a bit more complex
929 my $renamed_from = "";
930 $renamed_from = quote_table_name($diff_hash->{rename_table}[0][0]->name, $qt)
931 if $diff_hash->{rename_table} && @{$diff_hash->{rename_table}};
933 return unless @stmts;
934 # Just zero or one stmts. return now
935 return (@drop_stmt,@stmts) unless @stmts > 1;
937 # Now strip off the 'ALTER TABLE xyz' of all but the first one
939 my $table_name = quote_table_name($table->name, $qt);
941 my $re = $renamed_from
942 ? qr/^ALTER TABLE (?:\Q$table_name\E|\Q$renamed_from\E) /
943 : qr/^ALTER TABLE \Q$table_name\E /;
945 my $first = shift @stmts;
946 my ($alter_table) = $first =~ /($re)/;
948 my $padd = " " x length($alter_table);
950 return @drop_stmt, join( ",\n", $first, map { s/$re//; $padd . $_ } @stmts);
955 my ($table, $options) = @_;
957 my $qt = $options->{quote_table_names} || '';
959 # Drop (foreign key) constraints so table drops cleanly
960 my @sql = batch_alter_table($table, { alter_drop_constraint => [ grep { $_->type eq 'FOREIGN KEY' } $table->get_constraints ] }, $options);
962 my $table_name = quote_table_name($table, $qt);
963 return (@sql, "DROP TABLE $table");
968 my ($old_table, $new_table, $options) = @_;
970 my $qt = $options->{quote_table_names} || '';
971 my $old_table_name = quote_table_name($old_table, $qt);
972 my $new_table_name = quote_table_name($new_table, $qt);
974 return "ALTER TABLE $old_table_name RENAME TO $new_table_name";
977 sub next_unused_name {
978 my $name = shift || '';
979 if ( !defined($used_names{$name}) ) {
980 $used_names{$name} = $name;
985 while ( defined($used_names{$name . '_' . $i}) ) {
989 $used_names{$name} = $name;
999 SQL::Translator, http://www.mysql.com/.
1003 darren chamberlain E<lt>darren@cpan.orgE<gt>,
1004 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.