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 $field_def .= ' NOT NULL' unless $field->is_nullable;
618 SQL::Translator::Producer->_apply_default_value(
626 if ( my $comments = $field->comments ) {
627 $field_def .= qq[ comment '$comments'];
631 $field_def .= " auto_increment" if $field->is_auto_increment;
636 sub alter_create_index
638 my ($index, $options) = @_;
640 my $qt = $options->{quote_table_names} || '';
641 my $qf = $options->{quote_field_names} || '';
642 my $table_name = quote_table_name($index->table->name, $qt);
653 my ( $index, $options ) = @_;
655 my $qf = $options->{quote_field_names} || '';
660 lc $index->type eq 'normal' ? 'INDEX' : $index->type . ' INDEX',
662 ? $qf . truncate_id_uniquely(
664 $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH
667 '(' . $qf . join( "$qf, $qf", $index->fields ) . $qf . ')'
673 my ($index, $options) = @_;
675 my $qt = $options->{quote_table_names} || '';
676 my $qf = $options->{quote_field_names} || '';
677 my $table_name = quote_table_name($index->table->name, $qt);
684 $index->name || $index->fields
689 sub alter_drop_constraint
691 my ($c, $options) = @_;
693 my $qt = $options->{quote_table_names} || '';
694 my $qc = $options->{quote_field_names} || '';
695 my $table_name = quote_table_name($c->table->name, $qt);
697 my @out = ('ALTER','TABLE',$table_name,'DROP');
698 if($c->type eq PRIMARY_KEY) {
702 push @out, ($c->type eq FOREIGN_KEY ? $c->type : "INDEX"),
703 $qc . $c->name . $qc;
705 return join(' ',@out);
708 sub alter_create_constraint
710 my ($index, $options) = @_;
712 my $qt = $options->{quote_table_names} || '';
713 my $table_name = quote_table_name($index->table->name, $qt);
718 create_constraint(@_) );
721 sub create_constraint
723 my ($c, $options) = @_;
725 my $qf = $options->{quote_field_names} || '';
726 my $qt = $options->{quote_table_names} || '';
727 my $leave_name = $options->{leave_name} || undef;
729 my $reference_table_name = quote_table_name($c->reference_table, $qt);
731 my @fields = $c->fields or next;
733 if ( $c->type eq PRIMARY_KEY ) {
734 return 'PRIMARY KEY (' . $qf . join("$qf, $qf", @fields). $qf . ')';
736 elsif ( $c->type eq UNIQUE ) {
737 return sprintf 'UNIQUE %s(%s)',
738 ((defined $c->name && $c->name)
741 truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH ),
747 ( join ', ', map { "${qf}${_}${qf}" } @fields ),
750 elsif ( $c->type eq FOREIGN_KEY ) {
752 # Make sure FK field is indexed or MySQL complains.
755 my $table = $c->table;
756 my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH );
766 $def .= ' ('.$qf . join( "$qf, $qf", @fields ) . $qf . ')';
768 $def .= ' REFERENCES ' . $reference_table_name;
770 my @rfields = map { $_ || () } $c->reference_fields;
771 unless ( @rfields ) {
772 my $rtable_name = $c->reference_table;
773 if ( my $ref_table = $table->schema->get_table( $rtable_name ) ) {
774 push @rfields, $ref_table->primary_key;
777 warn "Can't find reference table '$rtable_name' " .
778 "in schema\n" if $options->{show_warnings};
783 $def .= ' (' . $qf . join( "$qf, $qf", @rfields ) . $qf . ')';
786 warn "FK constraint on " . $table->name . '.' .
787 join('', @fields) . " has no reference fields\n"
788 if $options->{show_warnings};
791 if ( $c->match_type ) {
793 ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
796 if ( $c->on_delete ) {
797 $def .= ' ON DELETE '. $c->on_delete;
800 if ( $c->on_update ) {
801 $def .= ' ON UPDATE '. $c->on_update;
811 my ($to_table, $options) = @_;
813 my $qt = $options->{quote_table_names} || '';
815 my $table_options = generate_table_options($to_table, $options) || '';
816 my $table_name = quote_table_name($to_table->name, $qt);
817 my $out = sprintf('ALTER TABLE %s%s',
824 sub rename_field { alter_field(@_) }
827 my ($from_field, $to_field, $options) = @_;
829 my $qf = $options->{quote_field_names} || '';
830 my $qt = $options->{quote_table_names} || '';
831 my $table_name = quote_table_name($to_field->table->name, $qt);
833 my $out = sprintf('ALTER TABLE %s CHANGE COLUMN %s %s',
835 $qf . $from_field->name . $qf,
836 create_field($to_field, $options));
843 my ($new_field, $options) = @_;
845 my $qt = $options->{quote_table_names} || '';
846 my $table_name = quote_table_name($new_field->table->name, $qt);
848 my $out = sprintf('ALTER TABLE %s ADD COLUMN %s',
850 create_field($new_field, $options));
858 my ($old_field, $options) = @_;
860 my $qf = $options->{quote_field_names} || '';
861 my $qt = $options->{quote_table_names} || '';
862 my $table_name = quote_table_name($old_field->table->name, $qt);
864 my $out = sprintf('ALTER TABLE %s DROP COLUMN %s',
866 $qf . $old_field->name . $qf);
872 sub batch_alter_table {
873 my ($table, $diff_hash, $options) = @_;
875 # InnoDB has an issue with dropping and re-adding a FK constraint under the
876 # name in a single alter statment, see: http://bugs.mysql.com/bug.php?id=13741
878 # We have to work round this.
881 my %fks_to_drop = map {
882 $_->type eq FOREIGN_KEY
885 } @{$diff_hash->{alter_drop_constraint} };
887 my %fks_to_create = map {
888 if ( $_->type eq FOREIGN_KEY) {
889 $fks_to_alter{$_->name} = $fks_to_drop{$_->name} if $fks_to_drop{$_->name};
892 } @{$diff_hash->{alter_create_constraint} };
895 if (scalar keys %fks_to_alter) {
896 $diff_hash->{alter_drop_constraint} = [
897 grep { !$fks_to_alter{$_->name} } @{ $diff_hash->{alter_drop_constraint} }
900 @drop_stmt = batch_alter_table($table, { alter_drop_constraint => [ values %fks_to_alter ] }, $options);
905 if (@{ $diff_hash->{$_} || [] }) {
906 my $meth = __PACKAGE__->can($_) or die __PACKAGE__ . " cant $_";
907 map { $meth->( (ref $_ eq 'ARRAY' ? @$_ : $_), $options ) } @{ $diff_hash->{$_} }
910 alter_drop_constraint
917 alter_create_constraint
921 my $qt = $options->{quote_table_names} || '';
923 # rename_table makes things a bit more complex
924 my $renamed_from = "";
925 $renamed_from = quote_table_name($diff_hash->{rename_table}[0][0]->name, $qt)
926 if $diff_hash->{rename_table} && @{$diff_hash->{rename_table}};
928 return unless @stmts;
929 # Just zero or one stmts. return now
930 return (@drop_stmt,@stmts) unless @stmts > 1;
932 # Now strip off the 'ALTER TABLE xyz' of all but the first one
934 my $table_name = quote_table_name($table->name, $qt);
936 my $re = $renamed_from
937 ? qr/^ALTER TABLE (?:\Q$table_name\E|\Q$renamed_from\E) /
938 : qr/^ALTER TABLE \Q$table_name\E /;
940 my $first = shift @stmts;
941 my ($alter_table) = $first =~ /($re)/;
943 my $padd = " " x length($alter_table);
945 return @drop_stmt, join( ",\n", $first, map { s/$re//; $padd . $_ } @stmts);
950 my ($table, $options) = @_;
952 my $qt = $options->{quote_table_names} || '';
954 # Drop (foreign key) constraints so table drops cleanly
955 my @sql = batch_alter_table($table, { alter_drop_constraint => [ grep { $_->type eq 'FOREIGN KEY' } $table->get_constraints ] }, $options);
957 my $table_name = quote_table_name($table, $qt);
958 return (@sql, "DROP TABLE $table");
963 my ($old_table, $new_table, $options) = @_;
965 my $qt = $options->{quote_table_names} || '';
966 my $old_table_name = quote_table_name($old_table, $qt);
967 my $new_table_name = quote_table_name($new_table, $qt);
969 return "ALTER TABLE $old_table_name RENAME TO $new_table_name";
972 sub next_unused_name {
973 my $name = shift || '';
974 if ( !defined($used_names{$name}) ) {
975 $used_names{$name} = $name;
980 while ( defined($used_names{$name . '_' . $i}) ) {
984 $used_names{$name} = $name;
994 SQL::Translator, http://www.mysql.com/.
998 darren chamberlain E<lt>darren@cpan.orgE<gt>,
999 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.