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.
86 This option allows to use a prefix of certain character type (eg. char,
87 varchar, text) fields in the index.
89 The value of this option is a hashref, keys are the field names, values are the
96 fields => [ 'id', 'name', 'address' ],
108 # It will generate the following SQL snippet in the table definition:
109 INDEX `idx1` (`id`, `name`(10), `address`(20)),
117 our ( $DEBUG, %used_names );
118 our $VERSION = '1.59';
119 $DEBUG = 0 unless defined $DEBUG;
121 # Maximum length for most identifiers is 64, according to:
122 # http://dev.mysql.com/doc/refman/4.1/en/identifiers.html
123 # http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
124 my $DEFAULT_MAX_ID_LENGTH = 64;
127 use List::Util qw(first);
128 use SQL::Translator::Schema::Constants;
129 use SQL::Translator::Utils qw(debug header_comment
130 truncate_id_uniquely parse_mysql_version);
133 # Use only lowercase for the keys (e.g. "long" and not "LONG")
139 varchar2 => 'varchar',
155 'long integer' => 'integer',
157 'datetime' => 'datetime',
166 # Column types that do not support lenth attribute
168 my @no_length_attr = qw/
169 date time timestamp datetime year
173 sub preprocess_schema {
176 # extra->{mysql_table_type} used to be the type. It belongs in options, so
177 # move it if we find it. Return Engine type if found in extra or options
178 # Similarly for mysql_charset and mysql_collate
179 my $extra_to_options = sub {
180 my ($table, $extra_name, $opt_name) = @_;
182 my $extra = $table->extra;
184 my $extra_type = delete $extra->{$extra_name};
186 # Now just to find if there is already an Engine or Type option...
187 # and lets normalize it to ENGINE since:
189 # The ENGINE table option specifies the storage engine for the table.
190 # TYPE is a synonym, but ENGINE is the preferred option name.
193 # We have to use the hash directly here since otherwise there is no way
195 my $options = ( $table->{options} ||= []);
197 # If multiple option names, normalize to the first one
199 OPT_NAME: for ( @$opt_name[1..$#$opt_name] ) {
200 for my $idx ( 0..$#{$options} ) {
201 my ($key, $value) = %{ $options->[$idx] };
204 $options->[$idx] = { $opt_name->[0] => $value };
209 $opt_name = $opt_name->[0];
214 # This assumes that there isn't both a Type and an Engine option.
216 for my $idx ( 0..$#{$options} ) {
217 my ($key, $value) = %{ $options->[$idx] };
219 next unless uc $key eq $opt_name;
221 # make sure case is right on option name
222 delete $options->[$idx]{$key};
223 return $options->[$idx]{$opt_name} = $value || $extra_type;
228 push @$options, { $opt_name => $extra_type };
234 # Names are only specific to a given schema
235 local %used_names = ();
238 # Work out which tables need to be InnoDB to support foreign key
239 # constraints. We do this first as we need InnoDB at both ends.
241 foreach my $table ( $schema->get_tables ) {
243 $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE'] );
244 $extra_to_options->($table, 'mysql_charset', 'CHARACTER SET' );
245 $extra_to_options->($table, 'mysql_collate', 'COLLATE' );
247 foreach my $c ( $table->get_constraints ) {
248 next unless $c->type eq FOREIGN_KEY;
250 # Normalize constraint names here.
251 my $c_name = $c->name;
252 # Give the constraint a name if it doesn't have one, so it doens't feel
254 $c_name = $table->name . '_fk' unless length $c_name;
256 $c->name( next_unused_name($c_name) );
258 for my $meth (qw/table reference_table/) {
259 my $table = $schema->get_table($c->$meth) || next;
260 # This normalizes the types to ENGINE and returns the value if its there
261 next if $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE']);
262 $table->options( { 'ENGINE' => 'InnoDB' } );
264 } # foreach constraints
266 my %map = ( mysql_collate => 'collate', mysql_charset => 'character set');
267 foreach my $f ( $table->get_fields ) {
268 my $extra = $f->extra;
270 $extra->{$map{$_}} = delete $extra->{$_} if exists $extra->{$_};
274 if ( !$size[0] && $f->data_type =~ /char$/ ) {
283 my $translator = shift;
284 local $DEBUG = $translator->debug;
286 my $no_comments = $translator->no_comments;
287 my $add_drop_table = $translator->add_drop_table;
288 my $schema = $translator->schema;
289 my $show_warnings = $translator->show_warnings || 0;
290 my $producer_args = $translator->producer_args;
291 my $mysql_version = parse_mysql_version ($producer_args->{mysql_version}, 'perl') || 0;
292 my $max_id_length = $producer_args->{mysql_max_id_length} || $DEFAULT_MAX_ID_LENGTH;
294 my ($qt, $qf, $qc) = ('','', '');
295 $qt = '`' if $translator->quote_table_names;
296 $qf = '`' if $translator->quote_field_names;
298 debug("PKG: Beginning production\n");
301 $create .= header_comment unless ($no_comments);
302 # \todo Don't set if MySQL 3.x is set on command line
303 my @create = "SET foreign_key_checks=0";
305 preprocess_schema($schema);
312 for my $table ( $schema->get_tables ) {
313 # print $table->name, "\n";
314 push @table_defs, create_table($table,
315 { add_drop_table => $add_drop_table,
316 show_warnings => $show_warnings,
317 no_comments => $no_comments,
318 quote_table_names => $qt,
319 quote_field_names => $qf,
320 max_id_length => $max_id_length,
321 mysql_version => $mysql_version
325 if ($mysql_version >= 5.000001) {
326 for my $view ( $schema->get_views ) {
327 push @table_defs, create_view($view,
328 { add_replace_view => $add_drop_table,
329 show_warnings => $show_warnings,
330 no_comments => $no_comments,
331 quote_table_names => $qt,
332 quote_field_names => $qf,
333 max_id_length => $max_id_length,
334 mysql_version => $mysql_version
339 if ($mysql_version >= 5.000002) {
340 for my $trigger ( $schema->get_triggers ) {
341 push @table_defs, create_trigger($trigger,
342 { add_drop_trigger => $add_drop_table,
343 show_warnings => $show_warnings,
344 no_comments => $no_comments,
345 quote_table_names => $qt,
346 quote_field_names => $qf,
347 max_id_length => $max_id_length,
348 mysql_version => $mysql_version
354 # print "@table_defs\n";
355 push @table_defs, "SET foreign_key_checks=1";
357 return wantarray ? ($create ? $create : (), @create, @table_defs) : ($create . join('', map { $_ ? "$_;\n\n" : () } (@create, @table_defs)));
361 my ($trigger, $options) = @_;
362 my $qt = $options->{quote_table_names} || '';
363 my $qf = $options->{quote_field_names} || '';
365 my $trigger_name = $trigger->name;
366 debug("PKG: Looking at trigger '${trigger_name}'\n");
370 my $events = $trigger->database_events;
371 for my $event ( @$events ) {
372 my $name = $trigger_name;
376 warn "Multiple database events supplied for trigger '${trigger_name}', ",
377 "creating trigger '${name}' for the '${event}' event\n"
378 if $options->{show_warnings};
381 my $action = $trigger->action;
382 $action .= ";" unless $action =~ /;\s*\z/;
384 push @statements, "DROP TRIGGER IF EXISTS ${qt}${name}${qt}" if $options->{add_drop_trigger};
385 push @statements, sprintf(
386 "CREATE TRIGGER ${qt}%s${qt} %s %s ON ${qt}%s${qt}\n FOR EACH ROW BEGIN %s END",
387 $name, $trigger->perform_action_when, $event, $trigger->on_table, $action,
391 # Tack the comment onto the first statement
392 $statements[0] = "--\n-- Trigger ${qt}${trigger_name}${qt}\n--\n" . $statements[0] unless $options->{no_comments};
397 my ($view, $options) = @_;
398 my $qt = $options->{quote_table_names} || '';
399 my $qf = $options->{quote_field_names} || '';
401 my $view_name = $view->name;
402 debug("PKG: Looking at view '${view_name}'\n");
404 # Header. Should this look like what mysqldump produces?
406 $create .= "--\n-- View: ${qt}${view_name}${qt}\n--\n" unless $options->{no_comments};
408 $create .= ' OR REPLACE' if $options->{add_replace_view};
411 my $extra = $view->extra;
413 if( exists($extra->{mysql_algorithm}) && defined(my $algorithm = $extra->{mysql_algorithm}) ){
414 $create .= " ALGORITHM = ${algorithm}\n" if $algorithm =~ /(?:UNDEFINED|MERGE|TEMPTABLE)/i;
417 if( exists($extra->{mysql_definer}) && defined(my $user = $extra->{mysql_definer}) ){
418 $create .= " DEFINER = ${user}\n";
421 if( exists($extra->{mysql_security}) && defined(my $security = $extra->{mysql_security}) ){
422 $create .= " SQL SECURITY ${security}\n" if $security =~ /(?:DEFINER|INVOKER)/i;
426 $create .= " VIEW ${qt}${view_name}${qt}";
428 if( my @fields = $view->fields ){
429 my $list = join ', ', map { "${qf}${_}${qf}"} @fields;
430 $create .= " ( ${list} )";
432 if( my $sql = $view->sql ){
433 # do not wrap parenthesis around the selector, mysql doesn't like this
434 # http://bugs.mysql.com/bug.php?id=9198
435 $create .= " AS\n ${sql}\n";
443 my ($table, $options) = @_;
445 my $qt = $options->{quote_table_names} || '';
446 my $qf = $options->{quote_field_names} || '';
448 my $table_name = quote_table_name($table->name, $qt);
449 debug("PKG: Looking at table '$table_name'\n");
452 # Header. Should this look like what mysqldump produces?
456 $create .= "--\n-- Table: $table_name\n--\n" unless $options->{no_comments};
457 $drop = qq[DROP TABLE IF EXISTS $table_name] if $options->{add_drop_table};
458 $create .= "CREATE TABLE $table_name (\n";
464 for my $field ( $table->get_fields ) {
465 push @field_defs, create_field($field, $options);
473 for my $index ( $table->get_indices ) {
474 push @index_defs, create_index($index, $options);
475 $indexed_fields{ $_ } = 1 for $index->fields;
479 # Constraints -- need to handle more than just FK. -ky
482 my @constraints = $table->get_constraints;
483 for my $c ( @constraints ) {
484 my $constr = create_constraint($c, $options);
485 push @constraint_defs, $constr if($constr);
487 unless ( $indexed_fields{ ($c->fields())[0] } || $c->type ne FOREIGN_KEY ) {
488 push @index_defs, "INDEX ($qf" . ($c->fields())[0] . "$qf)";
489 $indexed_fields{ ($c->fields())[0] } = 1;
493 $create .= join(",\n", map { " $_" }
494 @field_defs, @index_defs, @constraint_defs
501 $create .= generate_table_options($table, $options) || '';
502 # $create .= ";\n\n";
504 return $drop ? ($drop,$create) : $create;
507 sub quote_table_name {
508 my ($table_name, $qt) = @_;
510 $table_name =~ s/\./$qt.$qt/g;
512 return "$qt$table_name$qt";
515 sub generate_table_options
517 my ($table, $options) = @_;
520 my $table_type_defined = 0;
521 my $qf = $options->{quote_field_names} ||= '';
522 my $charset = $table->extra('mysql_charset');
523 my $collate = $table->extra('mysql_collate');
525 for my $t1_option_ref ( $table->options ) {
526 my($key, $value) = %{$t1_option_ref};
527 $table_type_defined = 1
528 if uc $key eq 'ENGINE' or uc $key eq 'TYPE';
529 if (uc $key eq 'CHARACTER SET') {
532 } elsif (uc $key eq 'COLLATE') {
535 } elsif (uc $key eq 'UNION') {
536 $union = "($qf". join("$qf, $qf", @$value) ."$qf)";
539 $create .= " $key=$value";
542 my $mysql_table_type = $table->extra('mysql_table_type');
543 $create .= " ENGINE=$mysql_table_type"
544 if $mysql_table_type && !$table_type_defined;
545 my $comments = $table->comments;
547 $create .= " DEFAULT CHARACTER SET $charset" if $charset;
548 $create .= " COLLATE $collate" if $collate;
549 $create .= " UNION=$union" if $union;
550 $create .= qq[ comment='$comments'] if $comments;
556 my ($field, $options) = @_;
558 my $qf = $options->{quote_field_names} ||= '';
560 my $field_name = $field->name;
561 debug("PKG: Looking at field '$field_name'\n");
562 my $field_def = "$qf$field_name$qf";
565 my $data_type = $field->data_type;
566 my @size = $field->size;
567 my %extra = $field->extra;
568 my $list = $extra{'list'} || [];
569 # \todo deal with embedded quotes
570 my $commalist = join( ', ', map { qq['$_'] } @$list );
571 my $charset = $extra{'mysql_charset'};
572 my $collate = $extra{'mysql_collate'};
574 my $mysql_version = $options->{mysql_version} || 0;
576 # Oracle "number" type -- figure best MySQL type
578 if ( lc $data_type eq 'number' ) {
580 if ( scalar @size > 1 ) {
581 $data_type = 'double';
583 elsif ( $size[0] && $size[0] >= 12 ) {
584 $data_type = 'bigint';
586 elsif ( $size[0] && $size[0] <= 1 ) {
587 $data_type = 'tinyint';
594 # Convert a large Oracle varchar to "text"
595 # (not necessary as of 5.0.3 http://dev.mysql.com/doc/refman/5.0/en/char.html)
597 elsif ( $data_type =~ /char/i && $size[0] > 255 ) {
598 unless ($size[0] <= 65535 && $mysql_version >= 5.000003 ) {
603 elsif ( $data_type =~ /boolean/i ) {
604 if ($mysql_version >= 4) {
605 $data_type = 'boolean';
608 $commalist = "'0','1'";
611 elsif ( exists $translate{ lc $data_type } ) {
612 $data_type = $translate{ lc $data_type };
615 @size = () if $data_type =~ /(text|blob)/i;
617 if ( $data_type =~ /(double|float)/ && scalar @size == 1 ) {
621 $field_def .= " $data_type";
623 if ( lc($data_type) eq 'enum' || lc($data_type) eq 'set') {
624 $field_def .= '(' . $commalist . ')';
627 defined $size[0] && $size[0] > 0
629 ! grep lc($data_type) eq $_, @no_length_attr
631 $field_def .= '(' . join( ', ', @size ) . ')';
635 $field_def .= " CHARACTER SET $charset" if $charset;
636 $field_def .= " COLLATE $collate" if $collate;
639 for my $qual ( qw[ binary unsigned zerofill ] ) {
640 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
641 $field_def .= " $qual";
643 for my $qual ( 'character set', 'collate', 'on update' ) {
644 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
645 $field_def .= " $qual $val";
649 if ( $field->is_nullable ) {
650 $field_def .= ' NULL';
653 $field_def .= ' NOT NULL';
657 SQL::Translator::Producer->_apply_default_value(
665 if ( my $comments = $field->comments ) {
666 $field_def .= qq[ comment '$comments'];
670 $field_def .= " auto_increment" if $field->is_auto_increment;
675 sub alter_create_index
677 my ($index, $options) = @_;
679 my $qt = $options->{quote_table_names} || '';
680 my $qf = $options->{quote_field_names} || '';
681 my $table_name = quote_table_name($index->table->name, $qt);
692 my ( $index, $options ) = @_;
694 my $qf = $options->{quote_field_names} || '';
696 my ($prefix_length) = map { ( $_ || {} )->{prefix_length} || {} }
697 first { ref $_ eq 'HASH' && exists $_->{prefix_length} }
701 "$qf$_$qf" . (defined $prefix_length->{$_} ? "($prefix_length->{$_})" : "")
707 lc $index->type eq 'normal' ? 'INDEX' : $index->type . ' INDEX',
709 ? $qf . truncate_id_uniquely(
711 $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH
714 '(' . join(", ", @fields) . ')'
720 my ($index, $options) = @_;
722 my $qt = $options->{quote_table_names} || '';
723 my $qf = $options->{quote_field_names} || '';
724 my $table_name = quote_table_name($index->table->name, $qt);
731 $index->name || $index->fields
736 sub alter_drop_constraint
738 my ($c, $options) = @_;
740 my $qt = $options->{quote_table_names} || '';
741 my $qc = $options->{quote_field_names} || '';
742 my $table_name = quote_table_name($c->table->name, $qt);
744 my @out = ('ALTER','TABLE',$table_name,'DROP');
745 if($c->type eq PRIMARY_KEY) {
749 push @out, ($c->type eq FOREIGN_KEY ? $c->type : "INDEX"),
750 $qc . $c->name . $qc;
752 return join(' ',@out);
755 sub alter_create_constraint
757 my ($index, $options) = @_;
759 my $qt = $options->{quote_table_names} || '';
760 my $table_name = quote_table_name($index->table->name, $qt);
765 create_constraint(@_) );
768 sub create_constraint
770 my ($c, $options) = @_;
772 my $qf = $options->{quote_field_names} || '';
773 my $qt = $options->{quote_table_names} || '';
774 my $leave_name = $options->{leave_name} || undef;
776 my $reference_table_name = quote_table_name($c->reference_table, $qt);
778 my @fields = $c->fields or next;
780 if ( $c->type eq PRIMARY_KEY ) {
781 return 'PRIMARY KEY (' . $qf . join("$qf, $qf", @fields). $qf . ')';
783 elsif ( $c->type eq UNIQUE ) {
784 return sprintf 'UNIQUE %s(%s)',
785 ((defined $c->name && $c->name)
788 truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH ),
794 ( join ', ', map { "${qf}${_}${qf}" } @fields ),
797 elsif ( $c->type eq FOREIGN_KEY ) {
799 # Make sure FK field is indexed or MySQL complains.
802 my $table = $c->table;
803 my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH );
813 $def .= ' ('.$qf . join( "$qf, $qf", @fields ) . $qf . ')';
815 $def .= ' REFERENCES ' . $reference_table_name;
817 my @rfields = map { $_ || () } $c->reference_fields;
818 unless ( @rfields ) {
819 my $rtable_name = $c->reference_table;
820 if ( my $ref_table = $table->schema->get_table( $rtable_name ) ) {
821 push @rfields, $ref_table->primary_key;
824 warn "Can't find reference table '$rtable_name' " .
825 "in schema\n" if $options->{show_warnings};
830 $def .= ' (' . $qf . join( "$qf, $qf", @rfields ) . $qf . ')';
833 warn "FK constraint on " . $table->name . '.' .
834 join('', @fields) . " has no reference fields\n"
835 if $options->{show_warnings};
838 if ( $c->match_type ) {
840 ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
843 if ( $c->on_delete ) {
844 $def .= ' ON DELETE '. $c->on_delete;
847 if ( $c->on_update ) {
848 $def .= ' ON UPDATE '. $c->on_update;
858 my ($to_table, $options) = @_;
860 my $qt = $options->{quote_table_names} || '';
862 my $table_options = generate_table_options($to_table, $options) || '';
863 my $table_name = quote_table_name($to_table->name, $qt);
864 my $out = sprintf('ALTER TABLE %s%s',
871 sub rename_field { alter_field(@_) }
874 my ($from_field, $to_field, $options) = @_;
876 my $qf = $options->{quote_field_names} || '';
877 my $qt = $options->{quote_table_names} || '';
878 my $table_name = quote_table_name($to_field->table->name, $qt);
880 my $out = sprintf('ALTER TABLE %s CHANGE COLUMN %s %s',
882 $qf . $from_field->name . $qf,
883 create_field($to_field, $options));
890 my ($new_field, $options) = @_;
892 my $qt = $options->{quote_table_names} || '';
893 my $table_name = quote_table_name($new_field->table->name, $qt);
895 my $out = sprintf('ALTER TABLE %s ADD COLUMN %s',
897 create_field($new_field, $options));
905 my ($old_field, $options) = @_;
907 my $qf = $options->{quote_field_names} || '';
908 my $qt = $options->{quote_table_names} || '';
909 my $table_name = quote_table_name($old_field->table->name, $qt);
911 my $out = sprintf('ALTER TABLE %s DROP COLUMN %s',
913 $qf . $old_field->name . $qf);
919 sub batch_alter_table {
920 my ($table, $diff_hash, $options) = @_;
922 # InnoDB has an issue with dropping and re-adding a FK constraint under the
923 # name in a single alter statment, see: http://bugs.mysql.com/bug.php?id=13741
925 # We have to work round this.
928 my %fks_to_drop = map {
929 $_->type eq FOREIGN_KEY
932 } @{$diff_hash->{alter_drop_constraint} };
934 my %fks_to_create = map {
935 if ( $_->type eq FOREIGN_KEY) {
936 $fks_to_alter{$_->name} = $fks_to_drop{$_->name} if $fks_to_drop{$_->name};
939 } @{$diff_hash->{alter_create_constraint} };
942 if (scalar keys %fks_to_alter) {
943 $diff_hash->{alter_drop_constraint} = [
944 grep { !$fks_to_alter{$_->name} } @{ $diff_hash->{alter_drop_constraint} }
947 @drop_stmt = batch_alter_table($table, { alter_drop_constraint => [ values %fks_to_alter ] }, $options);
952 if (@{ $diff_hash->{$_} || [] }) {
953 my $meth = __PACKAGE__->can($_) or die __PACKAGE__ . " cant $_";
954 map { $meth->( (ref $_ eq 'ARRAY' ? @$_ : $_), $options ) } @{ $diff_hash->{$_} }
957 alter_drop_constraint
964 alter_create_constraint
968 my $qt = $options->{quote_table_names} || '';
970 # rename_table makes things a bit more complex
971 my $renamed_from = "";
972 $renamed_from = quote_table_name($diff_hash->{rename_table}[0][0]->name, $qt)
973 if $diff_hash->{rename_table} && @{$diff_hash->{rename_table}};
975 return unless @stmts;
976 # Just zero or one stmts. return now
977 return (@drop_stmt,@stmts) unless @stmts > 1;
979 # Now strip off the 'ALTER TABLE xyz' of all but the first one
981 my $table_name = quote_table_name($table->name, $qt);
983 my $re = $renamed_from
984 ? qr/^ALTER TABLE (?:\Q$table_name\E|\Q$renamed_from\E) /
985 : qr/^ALTER TABLE \Q$table_name\E /;
987 my $first = shift @stmts;
988 my ($alter_table) = $first =~ /($re)/;
990 my $padd = " " x length($alter_table);
992 return @drop_stmt, join( ",\n", $first, map { s/$re//; $padd . $_ } @stmts);
997 my ($table, $options) = @_;
999 my $qt = $options->{quote_table_names} || '';
1001 # Drop (foreign key) constraints so table drops cleanly
1002 my @sql = batch_alter_table($table, { alter_drop_constraint => [ grep { $_->type eq 'FOREIGN KEY' } $table->get_constraints ] }, $options);
1004 my $table_name = quote_table_name($table, $qt);
1005 return (@sql, "DROP TABLE $table");
1010 my ($old_table, $new_table, $options) = @_;
1012 my $qt = $options->{quote_table_names} || '';
1013 my $old_table_name = quote_table_name($old_table, $qt);
1014 my $new_table_name = quote_table_name($new_table, $qt);
1016 return "ALTER TABLE $old_table_name RENAME TO $new_table_name";
1019 sub next_unused_name {
1020 my $name = shift || '';
1021 if ( !defined($used_names{$name}) ) {
1022 $used_names{$name} = $name;
1027 while ( defined($used_names{$name . '_' . $i}) ) {
1031 $used_names{$name} = $name;
1041 SQL::Translator, http://www.mysql.com/.
1045 darren chamberlain E<lt>darren@cpan.orgE<gt>,
1046 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.