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 character set and collation order.
74 =item B<field.mysql_charset>, B<field.mysql_collate>
76 Set the fields character 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 length 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 my $options = $table->options;
161 # If multiple option names, normalize to the first one
163 OPT_NAME: for ( @$opt_name[1..$#$opt_name] ) {
164 for my $idx ( 0..$#{$options} ) {
165 my ($key, $value) = %{ $options->[$idx] };
168 $options->[$idx] = { $opt_name->[0] => $value };
173 $opt_name = $opt_name->[0];
178 # This assumes that there isn't both a Type and an Engine option.
180 for my $idx ( 0..$#{$options} ) {
181 my ($key, $value) = %{ $options->[$idx] };
183 next unless uc $key eq $opt_name;
185 # make sure case is right on option name
186 delete $options->[$idx]{$key};
187 return $options->[$idx]{$opt_name} = $value || $extra_type;
192 push @$options, { $opt_name => $extra_type };
198 # Names are only specific to a given schema
199 local %used_names = ();
202 # Work out which tables need to be InnoDB to support foreign key
203 # constraints. We do this first as we need InnoDB at both ends.
205 foreach my $table ( $schema->get_tables ) {
207 $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE'] );
208 $extra_to_options->($table, 'mysql_charset', 'CHARACTER SET' );
209 $extra_to_options->($table, 'mysql_collate', 'COLLATE' );
211 foreach my $c ( $table->get_constraints ) {
212 next unless $c->type eq FOREIGN_KEY;
214 # Normalize constraint names here.
215 my $c_name = $c->name;
216 # Give the constraint a name if it doesn't have one, so it doesn't feel
218 $c_name = $table->name . '_fk' unless length $c_name;
220 $c->name( next_unused_name($c_name) );
222 for my $meth (qw/table reference_table/) {
223 my $table = $schema->get_table($c->$meth) || next;
224 # This normalizes the types to ENGINE and returns the value if its there
225 next if $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE']);
226 $table->options( { 'ENGINE' => 'InnoDB' } );
228 } # foreach constraints
230 my %map = ( mysql_collate => 'collate', mysql_charset => 'character set');
231 foreach my $f ( $table->get_fields ) {
232 my $extra = $f->extra;
234 $extra->{$map{$_}} = delete $extra->{$_} if exists $extra->{$_};
238 if ( !$size[0] && $f->data_type =~ /char$/ ) {
247 my $translator = shift;
248 local $DEBUG = $translator->debug;
250 my $no_comments = $translator->no_comments;
251 my $add_drop_table = $translator->add_drop_table;
252 my $schema = $translator->schema;
253 my $show_warnings = $translator->show_warnings || 0;
254 my $producer_args = $translator->producer_args;
255 my $mysql_version = parse_mysql_version ($producer_args->{mysql_version}, 'perl') || 0;
256 my $max_id_length = $producer_args->{mysql_max_id_length} || $DEFAULT_MAX_ID_LENGTH;
258 my ($qt, $qf, $qc) = ('','', '');
259 $qt = '`' if $translator->quote_table_names;
260 $qf = '`' if $translator->quote_field_names;
262 debug("PKG: Beginning production\n");
265 $create .= header_comment unless ($no_comments);
266 # \todo Don't set if MySQL 3.x is set on command line
267 my @create = "SET foreign_key_checks=0";
269 preprocess_schema($schema);
276 for my $table ( $schema->get_tables ) {
277 # print $table->name, "\n";
278 push @table_defs, create_table($table,
279 { add_drop_table => $add_drop_table,
280 show_warnings => $show_warnings,
281 no_comments => $no_comments,
282 quote_table_names => $qt,
283 quote_field_names => $qf,
284 max_id_length => $max_id_length,
285 mysql_version => $mysql_version
289 if ($mysql_version >= 5.000001) {
290 for my $view ( $schema->get_views ) {
291 push @table_defs, create_view($view,
292 { add_replace_view => $add_drop_table,
293 show_warnings => $show_warnings,
294 no_comments => $no_comments,
295 quote_table_names => $qt,
296 quote_field_names => $qf,
297 max_id_length => $max_id_length,
298 mysql_version => $mysql_version
303 if ($mysql_version >= 5.000002) {
304 for my $trigger ( $schema->get_triggers ) {
305 push @table_defs, create_trigger($trigger,
306 { add_drop_trigger => $add_drop_table,
307 show_warnings => $show_warnings,
308 no_comments => $no_comments,
309 quote_table_names => $qt,
310 quote_field_names => $qf,
311 max_id_length => $max_id_length,
312 mysql_version => $mysql_version
318 # print "@table_defs\n";
319 push @table_defs, "SET foreign_key_checks=1";
321 return wantarray ? ($create ? $create : (), @create, @table_defs) : ($create . join('', map { $_ ? "$_;\n\n" : () } (@create, @table_defs)));
325 my ($trigger, $options) = @_;
326 my $qt = $options->{quote_table_names} || '';
327 my $qf = $options->{quote_field_names} || '';
329 my $trigger_name = $trigger->name;
330 debug("PKG: Looking at trigger '${trigger_name}'\n");
334 my $events = $trigger->database_events;
335 for my $event ( @$events ) {
336 my $name = $trigger_name;
340 warn "Multiple database events supplied for trigger '${trigger_name}', ",
341 "creating trigger '${name}' for the '${event}' event\n"
342 if $options->{show_warnings};
345 my $action = $trigger->action;
346 $action .= ";" unless $action =~ /;\s*\z/;
348 push @statements, "DROP TRIGGER IF EXISTS ${qt}${name}${qt}" if $options->{add_drop_trigger};
349 push @statements, sprintf(
350 "CREATE TRIGGER ${qt}%s${qt} %s %s ON ${qt}%s${qt}\n FOR EACH ROW BEGIN %s END",
351 $name, $trigger->perform_action_when, $event, $trigger->on_table, $action,
355 # Tack the comment onto the first statement
356 $statements[0] = "--\n-- Trigger ${qt}${trigger_name}${qt}\n--\n" . $statements[0] unless $options->{no_comments};
361 my ($view, $options) = @_;
362 my $qt = $options->{quote_table_names} || '';
363 my $qf = $options->{quote_field_names} || '';
365 my $view_name = $view->name;
366 debug("PKG: Looking at view '${view_name}'\n");
368 # Header. Should this look like what mysqldump produces?
370 $create .= "--\n-- View: ${qt}${view_name}${qt}\n--\n" unless $options->{no_comments};
372 $create .= ' OR REPLACE' if $options->{add_replace_view};
375 my $extra = $view->extra;
377 if( exists($extra->{mysql_algorithm}) && defined(my $algorithm = $extra->{mysql_algorithm}) ){
378 $create .= " ALGORITHM = ${algorithm}\n" if $algorithm =~ /(?:UNDEFINED|MERGE|TEMPTABLE)/i;
381 if( exists($extra->{mysql_definer}) && defined(my $user = $extra->{mysql_definer}) ){
382 $create .= " DEFINER = ${user}\n";
385 if( exists($extra->{mysql_security}) && defined(my $security = $extra->{mysql_security}) ){
386 $create .= " SQL SECURITY ${security}\n" if $security =~ /(?:DEFINER|INVOKER)/i;
390 $create .= " VIEW ${qt}${view_name}${qt}";
392 if( my @fields = $view->fields ){
393 my $list = join ', ', map { "${qf}${_}${qf}"} @fields;
394 $create .= " ( ${list} )";
396 if( my $sql = $view->sql ){
397 # do not wrap parenthesis around the selector, mysql doesn't like this
398 # http://bugs.mysql.com/bug.php?id=9198
399 $create .= " AS\n ${sql}\n";
407 my ($table, $options) = @_;
409 my $qt = $options->{quote_table_names} || '';
410 my $qf = $options->{quote_field_names} || '';
412 my $table_name = quote_table_name($table->name, $qt);
413 debug("PKG: Looking at table '$table_name'\n");
416 # Header. Should this look like what mysqldump produces?
420 $create .= "--\n-- Table: $table_name\n--\n" unless $options->{no_comments};
421 $drop = qq[DROP TABLE IF EXISTS $table_name] if $options->{add_drop_table};
422 $create .= "CREATE TABLE $table_name (\n";
428 for my $field ( $table->get_fields ) {
429 push @field_defs, create_field($field, $options);
437 for my $index ( $table->get_indices ) {
438 push @index_defs, create_index($index, $options);
439 $indexed_fields{ $_ } = 1 for $index->fields;
443 # Constraints -- need to handle more than just FK. -ky
446 my @constraints = $table->get_constraints;
447 for my $c ( @constraints ) {
448 my $constr = create_constraint($c, $options);
449 push @constraint_defs, $constr if($constr);
451 unless ( $indexed_fields{ ($c->fields())[0] } || $c->type ne FOREIGN_KEY ) {
452 push @index_defs, "INDEX ($qf" . ($c->fields())[0] . "$qf)";
453 $indexed_fields{ ($c->fields())[0] } = 1;
457 $create .= join(",\n", map { " $_" }
458 @field_defs, @index_defs, @constraint_defs
465 $create .= generate_table_options($table, $options) || '';
466 # $create .= ";\n\n";
468 return $drop ? ($drop,$create) : $create;
471 sub quote_table_name {
472 my ($table_name, $qt) = @_;
474 $table_name =~ s/\./$qt.$qt/g;
476 return "$qt$table_name$qt";
479 sub generate_table_options
481 my ($table, $options) = @_;
484 my $table_type_defined = 0;
485 my $qf = $options->{quote_field_names} ||= '';
486 my $charset = $table->extra('mysql_charset');
487 my $collate = $table->extra('mysql_collate');
489 for my $t1_option_ref ( $table->options ) {
490 my($key, $value) = %{$t1_option_ref};
491 $table_type_defined = 1
492 if uc $key eq 'ENGINE' or uc $key eq 'TYPE';
493 if (uc $key eq 'CHARACTER SET') {
496 } elsif (uc $key eq 'COLLATE') {
499 } elsif (uc $key eq 'UNION') {
500 $union = "($qf". join("$qf, $qf", @$value) ."$qf)";
503 $create .= " $key=$value";
506 my $mysql_table_type = $table->extra('mysql_table_type');
507 $create .= " ENGINE=$mysql_table_type"
508 if $mysql_table_type && !$table_type_defined;
509 my $comments = $table->comments;
511 $create .= " DEFAULT CHARACTER SET $charset" if $charset;
512 $create .= " COLLATE $collate" if $collate;
513 $create .= " UNION=$union" if $union;
514 $create .= qq[ comment='$comments'] if $comments;
520 my ($field, $options) = @_;
522 my $qf = $options->{quote_field_names} ||= '';
524 my $field_name = $field->name;
525 debug("PKG: Looking at field '$field_name'\n");
526 my $field_def = "$qf$field_name$qf";
529 my $data_type = $field->data_type;
530 my @size = $field->size;
531 my %extra = $field->extra;
532 my $list = $extra{'list'} || [];
533 # \todo deal with embedded quotes
534 my $commalist = join( ', ', map { qq['$_'] } @$list );
535 my $charset = $extra{'mysql_charset'};
536 my $collate = $extra{'mysql_collate'};
538 my $mysql_version = $options->{mysql_version} || 0;
540 # Oracle "number" type -- figure best MySQL type
542 if ( lc $data_type eq 'number' ) {
544 if ( scalar @size > 1 ) {
545 $data_type = 'double';
547 elsif ( $size[0] && $size[0] >= 12 ) {
548 $data_type = 'bigint';
550 elsif ( $size[0] && $size[0] <= 1 ) {
551 $data_type = 'tinyint';
558 # Convert a large Oracle varchar to "text"
559 # (not necessary as of 5.0.3 http://dev.mysql.com/doc/refman/5.0/en/char.html)
561 elsif ( $data_type =~ /char/i && $size[0] > 255 ) {
562 unless ($size[0] <= 65535 && $mysql_version >= 5.000003 ) {
567 elsif ( $data_type =~ /boolean/i ) {
568 if ($mysql_version >= 4) {
569 $data_type = 'boolean';
572 $commalist = "'0','1'";
575 elsif ( exists $translate{ lc $data_type } ) {
576 $data_type = $translate{ lc $data_type };
579 @size = () if $data_type =~ /(text|blob)/i;
581 if ( $data_type =~ /(double|float)/ && scalar @size == 1 ) {
585 $field_def .= " $data_type";
587 if ( lc($data_type) eq 'enum' || lc($data_type) eq 'set') {
588 $field_def .= '(' . $commalist . ')';
591 defined $size[0] && $size[0] > 0
593 ! grep lc($data_type) eq $_, @no_length_attr
595 $field_def .= '(' . join( ', ', @size ) . ')';
599 $field_def .= " CHARACTER SET $charset" if $charset;
600 $field_def .= " COLLATE $collate" if $collate;
603 for my $qual ( qw[ binary unsigned zerofill ] ) {
604 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
605 $field_def .= " $qual";
607 for my $qual ( 'character set', 'collate', 'on update' ) {
608 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
610 $field_def .= " $qual ${$val}";
613 $field_def .= " $qual $val";
618 if ( $field->is_nullable ) {
619 $field_def .= ' NULL';
622 $field_def .= ' NOT NULL';
626 SQL::Translator::Producer->_apply_default_value(
634 if ( my $comments = $field->comments ) {
635 $field_def .= qq[ comment '$comments'];
639 $field_def .= " auto_increment" if $field->is_auto_increment;
644 sub alter_create_index
646 my ($index, $options) = @_;
648 my $qt = $options->{quote_table_names} || '';
649 my $qf = $options->{quote_field_names} || '';
650 my $table_name = quote_table_name($index->table->name, $qt);
661 my ( $index, $options ) = @_;
663 my $qf = $options->{quote_field_names} || '';
668 lc $index->type eq 'normal' ? 'INDEX' : $index->type . ' INDEX',
670 ? $qf . truncate_id_uniquely(
672 $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH
675 '(' . $qf . join( "$qf, $qf", $index->fields ) . $qf . ')'
681 my ($index, $options) = @_;
683 my $qt = $options->{quote_table_names} || '';
684 my $qf = $options->{quote_field_names} || '';
685 my $table_name = quote_table_name($index->table->name, $qt);
692 $index->name || $index->fields
697 sub alter_drop_constraint
699 my ($c, $options) = @_;
701 my $qt = $options->{quote_table_names} || '';
702 my $qc = $options->{quote_field_names} || '';
703 my $table_name = quote_table_name($c->table->name, $qt);
705 my @out = ('ALTER','TABLE',$table_name,'DROP');
706 if($c->type eq PRIMARY_KEY) {
710 push @out, ($c->type eq FOREIGN_KEY ? $c->type : "INDEX"),
711 $qc . $c->name . $qc;
713 return join(' ',@out);
716 sub alter_create_constraint
718 my ($index, $options) = @_;
720 my $qt = $options->{quote_table_names} || '';
721 my $table_name = quote_table_name($index->table->name, $qt);
726 create_constraint(@_) );
729 sub create_constraint
731 my ($c, $options) = @_;
733 my $qf = $options->{quote_field_names} || '';
734 my $qt = $options->{quote_table_names} || '';
735 my $leave_name = $options->{leave_name} || undef;
737 my $reference_table_name = quote_table_name($c->reference_table, $qt);
739 my @fields = $c->fields or return;
741 if ( $c->type eq PRIMARY_KEY ) {
742 return 'PRIMARY KEY (' . $qf . join("$qf, $qf", @fields). $qf . ')';
744 elsif ( $c->type eq UNIQUE ) {
745 return sprintf 'UNIQUE %s(%s)',
746 ((defined $c->name && $c->name)
749 truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH ),
755 ( join ', ', map { "${qf}${_}${qf}" } @fields ),
758 elsif ( $c->type eq FOREIGN_KEY ) {
760 # Make sure FK field is indexed or MySQL complains.
763 my $table = $c->table;
764 my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH );
774 $def .= ' ('.$qf . join( "$qf, $qf", @fields ) . $qf . ')';
776 $def .= ' REFERENCES ' . $reference_table_name;
778 my @rfields = map { $_ || () } $c->reference_fields;
779 unless ( @rfields ) {
780 my $rtable_name = $c->reference_table;
781 if ( my $ref_table = $table->schema->get_table( $rtable_name ) ) {
782 push @rfields, $ref_table->primary_key;
785 warn "Can't find reference table '$rtable_name' " .
786 "in schema\n" if $options->{show_warnings};
791 $def .= ' (' . $qf . join( "$qf, $qf", @rfields ) . $qf . ')';
794 warn "FK constraint on " . $table->name . '.' .
795 join('', @fields) . " has no reference fields\n"
796 if $options->{show_warnings};
799 if ( $c->match_type ) {
801 ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
804 if ( $c->on_delete ) {
805 $def .= ' ON DELETE '. $c->on_delete;
808 if ( $c->on_update ) {
809 $def .= ' ON UPDATE '. $c->on_update;
819 my ($to_table, $options) = @_;
821 my $qt = $options->{quote_table_names} || '';
823 my $table_options = generate_table_options($to_table, $options) || '';
824 my $table_name = quote_table_name($to_table->name, $qt);
825 my $out = sprintf('ALTER TABLE %s%s',
832 sub rename_field { alter_field(@_) }
835 my ($from_field, $to_field, $options) = @_;
837 my $qf = $options->{quote_field_names} || '';
838 my $qt = $options->{quote_table_names} || '';
839 my $table_name = quote_table_name($to_field->table->name, $qt);
841 my $out = sprintf('ALTER TABLE %s CHANGE COLUMN %s %s',
843 $qf . $from_field->name . $qf,
844 create_field($to_field, $options));
851 my ($new_field, $options) = @_;
853 my $qt = $options->{quote_table_names} || '';
854 my $table_name = quote_table_name($new_field->table->name, $qt);
856 my $out = sprintf('ALTER TABLE %s ADD COLUMN %s',
858 create_field($new_field, $options));
866 my ($old_field, $options) = @_;
868 my $qf = $options->{quote_field_names} || '';
869 my $qt = $options->{quote_table_names} || '';
870 my $table_name = quote_table_name($old_field->table->name, $qt);
872 my $out = sprintf('ALTER TABLE %s DROP COLUMN %s',
874 $qf . $old_field->name . $qf);
880 sub batch_alter_table {
881 my ($table, $diff_hash, $options) = @_;
883 # InnoDB has an issue with dropping and re-adding a FK constraint under the
884 # name in a single alter statement, see: http://bugs.mysql.com/bug.php?id=13741
886 # We have to work round this.
889 my %fks_to_drop = map {
890 $_->type eq FOREIGN_KEY
893 } @{$diff_hash->{alter_drop_constraint} };
895 my %fks_to_create = map {
896 if ( $_->type eq FOREIGN_KEY) {
897 $fks_to_alter{$_->name} = $fks_to_drop{$_->name} if $fks_to_drop{$_->name};
900 } @{$diff_hash->{alter_create_constraint} };
903 if (scalar keys %fks_to_alter) {
904 $diff_hash->{alter_drop_constraint} = [
905 grep { !$fks_to_alter{$_->name} } @{ $diff_hash->{alter_drop_constraint} }
908 @drop_stmt = batch_alter_table($table, { alter_drop_constraint => [ values %fks_to_alter ] }, $options);
913 if (@{ $diff_hash->{$_} || [] }) {
914 my $meth = __PACKAGE__->can($_) or die __PACKAGE__ . " cant $_";
915 map { $meth->( (ref $_ eq 'ARRAY' ? @$_ : $_), $options ) } @{ $diff_hash->{$_} }
918 alter_drop_constraint
925 alter_create_constraint
929 my $qt = $options->{quote_table_names} || '';
931 # rename_table makes things a bit more complex
932 my $renamed_from = "";
933 $renamed_from = quote_table_name($diff_hash->{rename_table}[0][0]->name, $qt)
934 if $diff_hash->{rename_table} && @{$diff_hash->{rename_table}};
936 return unless @stmts;
937 # Just zero or one stmts. return now
938 return (@drop_stmt,@stmts) unless @stmts > 1;
940 # Now strip off the 'ALTER TABLE xyz' of all but the first one
942 my $table_name = quote_table_name($table->name, $qt);
944 my $re = $renamed_from
945 ? qr/^ALTER TABLE (?:\Q$table_name\E|\Q$renamed_from\E) /
946 : qr/^ALTER TABLE \Q$table_name\E /;
948 my $first = shift @stmts;
949 my ($alter_table) = $first =~ /($re)/;
951 my $padd = " " x length($alter_table);
953 return @drop_stmt, join( ",\n", $first, map { s/$re//; $padd . $_ } @stmts);
958 my ($table, $options) = @_;
960 my $qt = $options->{quote_table_names} || '';
962 # Drop (foreign key) constraints so table drops cleanly
963 my @sql = batch_alter_table($table, { alter_drop_constraint => [ grep { $_->type eq 'FOREIGN KEY' } $table->get_constraints ] }, $options);
965 my $table_name = quote_table_name($table, $qt);
966 return (@sql, "DROP TABLE $table");
971 my ($old_table, $new_table, $options) = @_;
973 my $qt = $options->{quote_table_names} || '';
974 my $old_table_name = quote_table_name($old_table, $qt);
975 my $new_table_name = quote_table_name($new_table, $qt);
977 return "ALTER TABLE $old_table_name RENAME TO $new_table_name";
980 sub next_unused_name {
981 my $name = shift || '';
982 if ( !defined($used_names{$name}) ) {
983 $used_names{$name} = $name;
988 while ( defined($used_names{$name . '_' . $i}) ) {
992 $used_names{$name} = $name;
1002 SQL::Translator, http://www.mysql.com/.
1006 darren chamberlain E<lt>darren@cpan.orgE<gt>,
1007 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.