File Coverage

blib/lib/SQL/Translator/Producer/MySQL.pm
Criterion Covered Total %
statement 382 395 96.7
branch 170 202 84.1
condition 75 102 73.5
subroutine 32 32 100.0
pod 10 22 45.4
total 669 753 88.8


line stmt bran cond sub pod time code
1             package SQL::Translator::Producer::MySQL;
2              
3             =head1 NAME
4              
5             SQL::Translator::Producer::MySQL - MySQL-specific producer for SQL::Translator
6              
7             =head1 SYNOPSIS
8              
9             Use via SQL::Translator:
10              
11             use SQL::Translator;
12              
13             my $t = SQL::Translator->new( parser => '...', producer => 'MySQL', '...' );
14             $t->translate;
15              
16             =head1 DESCRIPTION
17              
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
21             for fields, etc.).
22              
23             =head1 ARGUMENTS
24              
25             This producer takes a single optional producer_arg C, 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.
29              
30             Valid version specifiers for C are listed L
31              
32             =head2 Table Types
33              
34             Normally the tables will be created without any explicit table type given and
35             so will use the MySQL default.
36              
37             Any tables involved in foreign key constraints automatically get a table type
38             of InnoDB, unless this is overridden by setting the C extra
39             attribute explicitly on the table.
40              
41             =head2 Extra attributes.
42              
43             The producer recognises the following extra attributes on the Schema objects.
44              
45             =over 4
46              
47             =item B
48              
49             Set the list of allowed values for Enum fields.
50              
51             =item B, B, B
52              
53             Set the MySQL field options of the same name.
54              
55             =item B, B
56              
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.
59              
60             =item B
61              
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">.
65              
66             Please note that the C option is the preferred method of specifying
67             the MySQL storage engine to use, but this method still works for backwards
68             compatibility.
69              
70             =item B, B
71              
72             Set the tables default character set and collation order.
73              
74             =item B, B
75              
76             Set the fields character set and collation order.
77              
78             =back
79              
80             =cut
81              
82 12     12   2484 use strict;
  12         24  
  12         540  
83 12     12   62 use warnings;
  12         24  
  12         927  
84             our ( $DEBUG, %used_names );
85             our $VERSION = '1.6_3';
86             $DEBUG = 0 unless defined $DEBUG;
87              
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;
92              
93 12     12   210 use base qw(SQL::Translator::Producer);
  12         22  
  12         1747  
94 12     12   75 use Data::Dumper;
  12         24  
  12         832  
95 12     12   73 use SQL::Translator::Schema::Constants;
  12         22  
  12         843  
96 12     12   4763 use SQL::Translator::Generator::DDL::MySQL;
  12         37  
  12         445  
97 12         52152 use SQL::Translator::Utils qw(debug header_comment
98             truncate_id_uniquely parse_mysql_version
99             batch_alter_table_statements
100             normalize_quote_options
101 12     12   517 );
  12         26  
102              
103             #
104             # Use only lowercase for the keys (e.g. "long" and not "LONG")
105             #
106             my %translate = (
107             #
108             # Oracle types
109             #
110             varchar2 => 'varchar',
111             long => 'text',
112             clob => 'longtext',
113              
114             #
115             # Sybase types
116             #
117             int => 'integer',
118             money => 'float',
119             real => 'double',
120             comment => 'text',
121             bit => 'tinyint',
122              
123             #
124             # Access types
125             #
126             'long integer' => 'integer',
127             'text' => 'text',
128             'datetime' => 'datetime',
129              
130             #
131             # PostgreSQL types
132             #
133             bytea => 'BLOB',
134             );
135              
136             #
137             # Column types that do not support length attribute
138             #
139             my @no_length_attr = qw/
140             date time timestamp datetime year
141             /;
142              
143              
144             sub preprocess_schema {
145 50     50 0 124 my ($schema) = @_;
146              
147             # extra->{mysql_table_type} used to be the type. It belongs in options, so
148             # move it if we find it. Return Engine type if found in extra or options
149             # Similarly for mysql_charset and mysql_collate
150             my $extra_to_options = sub {
151 599     599   1058 my ($table, $extra_name, $opt_name) = @_;
152              
153 599         9784 my $extra = $table->extra;
154              
155 599         974 my $extra_type = delete $extra->{$extra_name};
156              
157             # Now just to find if there is already an Engine or Type option...
158             # and lets normalize it to ENGINE since:
159             #
160             # The ENGINE table option specifies the storage engine for the table.
161             # TYPE is a synonym, but ENGINE is the preferred option name.
162             #
163              
164 599         8850 my $options = $table->options;
165              
166             # If multiple option names, normalize to the first one
167 599 100       1220 if (ref $opt_name) {
168 303         779 OPT_NAME: for ( @$opt_name[1..$#$opt_name] ) {
169 303         413 for my $idx ( 0..$#{$options} ) {
  303         774  
170 263         366 my ($key, $value) = %{ $options->[$idx] };
  263         842  
171              
172 263 50       846 if (uc $key eq $_) {
173 0         0 $options->[$idx] = { $opt_name->[0] => $value };
174 0         0 last OPT_NAME;
175             }
176             }
177             }
178 303         536 $opt_name = $opt_name->[0];
179              
180             }
181              
182              
183             # This assumes that there isn't both a Type and an Engine option.
184             OPTION:
185 599         745 for my $idx ( 0..$#{$options} ) {
  599         1103  
186 453         571 my ($key, $value) = %{ $options->[$idx] };
  453         940  
187              
188 453 100       1089 next unless uc $key eq $opt_name;
189              
190             # make sure case is right on option name
191 239         431 delete $options->[$idx]{$key};
192 239   33     1016 return $options->[$idx]{$opt_name} = $value || $extra_type;
193              
194             }
195              
196 360 100       835 if ($extra_type) {
197 8         26 push @$options, { $opt_name => $extra_type };
198 8         26 return $extra_type;
199             }
200              
201 50         400 };
202              
203             # Names are only specific to a given schema
204 50         118 local %used_names = ();
205              
206             #
207             # Work out which tables need to be InnoDB to support foreign key
208             # constraints. We do this first as we need InnoDB at both ends.
209             #
210 50         237 foreach my $table ( $schema->get_tables ) {
211              
212 148         534 $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE'] );
213 148         430 $extra_to_options->($table, 'mysql_charset', 'CHARACTER SET' );
214 148         378 $extra_to_options->($table, 'mysql_collate', 'COLLATE' );
215              
216 148         537 foreach my $c ( $table->get_constraints ) {
217 291 100       7624 next unless $c->type eq FOREIGN_KEY;
218              
219             # Normalize constraint names here.
220 90         2910 my $c_name = $c->name;
221             # Give the constraint a name if it doesn't have one, so it doesn't feel
222             # left out
223 90 100       453 $c_name = $table->name . '_fk' unless length $c_name;
224              
225 90         470 $c->name( next_unused_name($c_name) );
226              
227 90         236 for my $meth (qw/table reference_table/) {
228 180   100     1996 my $table = $schema->get_table($c->$meth) || next;
229             # This normalizes the types to ENGINE and returns the value if its there
230 155 100       2652 next if $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE']);
231 12         215 $table->options( { 'ENGINE' => 'InnoDB' } );
232             }
233             } # foreach constraints
234              
235 148         1885 my %map = ( mysql_collate => 'collate', mysql_charset => 'character set');
236 148         551 foreach my $f ( $table->get_fields ) {
237 492         7472 my $extra = $f->extra;
238 492         1144 for (keys %map) {
239 984 100       1826 $extra->{$map{$_}} = delete $extra->{$_} if exists $extra->{$_};
240             }
241              
242 492         7522 my @size = $f->size;
243 492 100 100     5443 if ( !$size[0] && $f->data_type =~ /char$/ ) {
244 10         155 $f->size( (255) );
245             }
246             }
247              
248             }
249             }
250              
251             {
252             my ($quoting_generator, $nonquoting_generator);
253             sub _generator {
254 820     820   1749 my $options = shift;
255 820 100       2147 return $options->{generator} if exists $options->{generator};
256              
257 290 100 66     702 return normalize_quote_options($options)
      66        
258             ? $quoting_generator ||= SQL::Translator::Generator::DDL::MySQL->new()
259             : $nonquoting_generator ||= SQL::Translator::Generator::DDL::MySQL->new(
260             quote_chars => [],
261             );
262             }
263             }
264              
265             sub produce {
266 22     22 1 48 my $translator = shift;
267 22         84 local $DEBUG = $translator->debug;
268 22         201 local %used_names;
269 22         366 my $no_comments = $translator->no_comments;
270 22         531 my $add_drop_table = $translator->add_drop_table;
271 22         537 my $schema = $translator->schema;
272 22   100     496 my $show_warnings = $translator->show_warnings || 0;
273 22         551 my $producer_args = $translator->producer_args;
274 22   100     134 my $mysql_version = parse_mysql_version ($producer_args->{mysql_version}, 'perl') || 0;
275 22   33     118 my $max_id_length = $producer_args->{mysql_max_id_length} || $DEFAULT_MAX_ID_LENGTH;
276              
277 22         401 my $generator = _generator({ quote_identifiers => $translator->quote_identifiers });
278              
279 22         137 debug("PKG: Beginning production\n");
280 22         47 %used_names = ();
281 22         52 my $create = '';
282 22 100       72 $create .= header_comment unless ($no_comments);
283             # \todo Don't set if MySQL 3.x is set on command line
284 22         58 my @create = "SET foreign_key_checks=0";
285              
286 22         109 preprocess_schema($schema);
287              
288             #
289             # Generate sql
290             #
291 22         62 my @table_defs =();
292              
293 22         109 for my $table ( $schema->get_tables ) {
294             # print $table->name, "\n";
295 60         508 push @table_defs, create_table($table,
296             { add_drop_table => $add_drop_table,
297             show_warnings => $show_warnings,
298             no_comments => $no_comments,
299             generator => $generator,
300             max_id_length => $max_id_length,
301             mysql_version => $mysql_version
302             });
303             }
304              
305 22 100       108 if ($mysql_version >= 5.000001) {
306 2         8 for my $view ( $schema->get_views ) {
307 2         13 push @table_defs, create_view($view,
308             { add_replace_view => $add_drop_table,
309             show_warnings => $show_warnings,
310             no_comments => $no_comments,
311             generator => $generator,
312             max_id_length => $max_id_length,
313             mysql_version => $mysql_version
314             });
315             }
316             }
317              
318 22 100       84 if ($mysql_version >= 5.000002) {
319 2         10 for my $trigger ( $schema->get_triggers ) {
320 4         18 push @table_defs, create_trigger($trigger,
321             { add_drop_trigger => $add_drop_table,
322             show_warnings => $show_warnings,
323             no_comments => $no_comments,
324             generator => $generator,
325             max_id_length => $max_id_length,
326             mysql_version => $mysql_version
327             });
328             }
329             }
330              
331              
332             # print "@table_defs\n";
333 22         54 push @table_defs, "SET foreign_key_checks=1";
334              
335 22 100       145 return wantarray ? ($create ? $create : (), @create, @table_defs) : ($create . join('', map { $_ ? "$_;\n\n" : () } (@create, @table_defs)));
  105 50       2589  
    100          
336             }
337              
338             sub create_trigger {
339 6     6 1 19 my ($trigger, $options) = @_;
340 6         13 my $generator = _generator($options);
341              
342 6         17 my $trigger_name = $trigger->name;
343 6         23 debug("PKG: Looking at trigger '${trigger_name}'\n");
344              
345 6         11 my @statements;
346              
347 6         123 my $events = $trigger->database_events;
348 6         55 for my $event ( @$events ) {
349 8         13 my $name = $trigger_name;
350 8 100       19 if (@$events > 1) {
351 4         18 $name .= "_$event";
352              
353             warn "Multiple database events supplied for trigger '${trigger_name}', ",
354             "creating trigger '${name}' for the '${event}' event\n"
355 4 50       9 if $options->{show_warnings};
356             }
357              
358 8         55 my $action = $trigger->action;
359 8 100       38 if($action !~ /^ \s* BEGIN [\s\;] .*? [\s\;] END [\s\;]* $/six) {
360 7 100       29 $action .= ";" unless $action =~ /;\s*\z/;
361 7         18 $action = "BEGIN $action END";
362             }
363              
364 8 100       36 push @statements, "DROP TRIGGER IF EXISTS " . $generator->quote($name) if $options->{add_drop_trigger};
365 8         22 push @statements, sprintf(
366             "CREATE TRIGGER %s %s %s ON %s\n FOR EACH ROW %s",
367             $generator->quote($name), $trigger->perform_action_when, $event,
368             $generator->quote($trigger->on_table), $action,
369             );
370              
371             }
372             # Tack the comment onto the first statement
373 6 100       18 $statements[0] = "--\n-- Trigger " . $generator->quote($trigger_name) . "\n--\n" . $statements[0] unless $options->{no_comments};
374 6         23 return @statements;
375             }
376              
377             sub create_view {
378 6     6 1 126 my ($view, $options) = @_;
379 6         15 my $generator = _generator($options);
380              
381 6         19 my $view_name = $view->name;
382 6         19 my $view_name_qt = $generator->quote($view_name);
383              
384 6         72 debug("PKG: Looking at view '${view_name}'\n");
385              
386             # Header. Should this look like what mysqldump produces?
387 6         14 my $create = '';
388 6 50       17 $create .= "--\n-- View: $view_name_qt\n--\n" unless $options->{no_comments};
389 6         27 $create .= 'CREATE';
390 6 100       18 $create .= ' OR REPLACE' if $options->{add_replace_view};
391 6         8 $create .= "\n";
392              
393 6         116 my $extra = $view->extra;
394             # ALGORITHM
395 6 100 66     23 if( exists($extra->{mysql_algorithm}) && defined(my $algorithm = $extra->{mysql_algorithm}) ){
396 2 50       18 $create .= " ALGORITHM = ${algorithm}\n" if $algorithm =~ /(?:UNDEFINED|MERGE|TEMPTABLE)/i;
397             }
398             # DEFINER
399 6 100 66     19 if( exists($extra->{mysql_definer}) && defined(my $user = $extra->{mysql_definer}) ){
400 2         6 $create .= " DEFINER = ${user}\n";
401             }
402             # SECURITY
403 6 100 66     21 if( exists($extra->{mysql_security}) && defined(my $security = $extra->{mysql_security}) ){
404 2 50       16 $create .= " SQL SECURITY ${security}\n" if $security =~ /(?:DEFINER|INVOKER)/i;
405             }
406              
407             #Header, cont.
408 6         13 $create .= " VIEW $view_name_qt";
409              
410 6 50       101 if( my @fields = $view->fields ){
411 6         12 my $list = join ', ', map { $generator->quote($_) } @fields;
  10         33  
412 6         17 $create .= " ( ${list} )";
413             }
414 6 50       74 if( my $sql = $view->sql ){
415             # do not wrap parenthesis around the selector, mysql doesn't like this
416             # http://bugs.mysql.com/bug.php?id=9198
417 6         20 $create .= " AS\n ${sql}\n";
418             }
419             # $create .= "";
420 6         21 return $create;
421             }
422              
423             sub create_table
424             {
425 60     60 1 379 my ($table, $options) = @_;
426 60         179 my $generator = _generator($options);
427              
428 60         1203 my $table_name = $generator->quote($table->name);
429 60         361 debug("PKG: Looking at table '$table_name'\n");
430              
431             #
432             # Header. Should this look like what mysqldump produces?
433             #
434 60         134 my $create = '';
435 60         100 my $drop;
436 60 100       234 $create .= "--\n-- Table: $table_name\n--\n" unless $options->{no_comments};
437 60 100       221 $drop = qq[DROP TABLE IF EXISTS $table_name] if $options->{add_drop_table};
438 60         170 $create .= "CREATE TABLE $table_name (\n";
439              
440             #
441             # Fields
442             #
443 60         112 my @field_defs;
444 60         234 for my $field ( $table->get_fields ) {
445 224         617 push @field_defs, create_field($field, $options);
446             }
447              
448             #
449             # Indices
450             #
451 60         167 my @index_defs;
452             my %indexed_fields;
453 60         263 for my $index ( $table->get_indices ) {
454 46         332 push @index_defs, create_index($index, $options);
455 46         754 $indexed_fields{ $_ } = 1 for $index->fields;
456             }
457              
458             #
459             # Constraints -- need to handle more than just FK. -ky
460             #
461 60         134 my @constraint_defs;
462 60         218 my @constraints = $table->get_constraints;
463 60         511 for my $c ( @constraints ) {
464 134         2156 my $constr = create_constraint($c, $options);
465 134 100       629 push @constraint_defs, $constr if($constr);
466              
467 134 100 100     432 unless ( $indexed_fields{ ($c->fields())[0] } || $c->type ne FOREIGN_KEY ) {
468 32         655 push @index_defs, "INDEX (" . $generator->quote(($c->fields())[0]) . ")";
469 32         285 $indexed_fields{ ($c->fields())[0] } = 1;
470             }
471             }
472              
473 60         969 $create .= join(",\n", map { " $_" }
  418         961  
474             @field_defs, @index_defs, @constraint_defs
475             );
476              
477             #
478             # Footer
479             #
480 60         174 $create .= "\n)";
481 60   100     192 $create .= generate_table_options($table, $options) || '';
482             # $create .= ";\n\n";
483              
484 60 100       648 return $drop ? ($drop,$create) : $create;
485             }
486              
487             sub generate_table_options
488             {
489 65     65 0 151 my ($table, $options) = @_;
490 65         111 my $create;
491              
492 65         123 my $table_type_defined = 0;
493 65         177 my $generator = _generator($options);
494 65         1250 my $charset = $table->extra('mysql_charset');
495 65         1009 my $collate = $table->extra('mysql_collate');
496 65         131 my $union = undef;
497 65         1095 for my $t1_option_ref ( $table->options ) {
498 59         126 my($key, $value) = %{$t1_option_ref};
  59         207  
499 59 100 66     290 $table_type_defined = 1
500             if uc $key eq 'ENGINE' or uc $key eq 'TYPE';
501 59 100       294 if (uc $key eq 'CHARACTER SET') {
    100          
    50          
502 6         11 $charset = $value;
503 6         16 next;
504             } elsif (uc $key eq 'COLLATE') {
505 6         13 $collate = $value;
506 6         14 next;
507             } elsif (uc $key eq 'UNION') {
508 0         0 $union = '(' . join(', ', map { $generator->quote($_) } @$value) . ')';
  0         0  
509 0         0 next;
510             }
511 47         182 $create .= " $key=$value";
512             }
513              
514 65         1168 my $mysql_table_type = $table->extra('mysql_table_type');
515 65 50 33     229 $create .= " ENGINE=$mysql_table_type"
516             if $mysql_table_type && !$table_type_defined;
517 65         1096 my $comments = $table->comments;
518              
519 65 100       205 $create .= " DEFAULT CHARACTER SET $charset" if $charset;
520 65 100       174 $create .= " COLLATE $collate" if $collate;
521 65 50       147 $create .= " UNION=$union" if $union;
522 65 100       167 $create .= qq[ comment='$comments'] if $comments;
523 65         272 return $create;
524             }
525              
526             sub create_field
527             {
528 311     311 1 4557 my ($field, $options) = @_;
529              
530 311         632 my $generator = _generator($options);
531              
532 311         5321 my $field_name = $field->name;
533 311         5977 debug("PKG: Looking at field '$field_name'\n");
534 311         1053 my $field_def = $generator->quote($field_name);
535              
536             # data type and size
537 311         995 my $data_type = $field->data_type;
538 311         5130 my @size = $field->size;
539 311         6781 my %extra = $field->extra;
540 311   100     1250 my $list = $extra{'list'} || [];
541 311         809 my $commalist = join( ', ', map { __PACKAGE__->_quote_string($_) } @$list );
  36         81  
542 311         511 my $charset = $extra{'mysql_charset'};
543 311         435 my $collate = $extra{'mysql_collate'};
544              
545 311   100     982 my $mysql_version = $options->{mysql_version} || 0;
546             #
547             # Oracle "number" type -- figure best MySQL type
548             #
549 311 100 100     2335 if ( lc $data_type eq 'number' ) {
    100          
    100          
    100          
550             # not an integer
551 8 100 66     60 if ( scalar @size > 1 ) {
    100 66        
    100          
552 2         6 $data_type = 'double';
553             }
554             elsif ( $size[0] && $size[0] >= 12 ) {
555 2         5 $data_type = 'bigint';
556             }
557             elsif ( $size[0] && $size[0] <= 1 ) {
558 2         7 $data_type = 'tinyint';
559             }
560             else {
561 2         6 $data_type = 'int';
562             }
563             }
564             #
565             # Convert a large Oracle varchar to "text"
566             # (not necessary as of 5.0.3 http://dev.mysql.com/doc/refman/5.0/en/char.html)
567             #
568             elsif ( $data_type =~ /char/i && $size[0] > 255 ) {
569 24 100 100     130 unless ($size[0] <= 65535 && $mysql_version >= 5.000003 ) {
570 18         31 $data_type = 'text';
571 18         33 @size = ();
572             }
573             }
574             elsif ( $data_type =~ /boolean/i ) {
575 6 100       18 if ($mysql_version >= 4) {
576 2         6 $data_type = 'boolean';
577             } else {
578 4         5 $data_type = 'enum';
579 4         7 $commalist = "'0','1'";
580             }
581             }
582             elsif ( exists $translate{ lc $data_type } ) {
583 114         270 $data_type = $translate{ lc $data_type };
584             }
585              
586 311 100       1393 @size = () if $data_type =~ /(text|blob)/i;
587              
588 311 50 66     970 if ( $data_type =~ /(double|float)/ && scalar @size == 1 ) {
589 0         0 push @size, '0';
590             }
591              
592 311         1010 $field_def .= " $data_type";
593              
594 311 100 100     2454 if ( lc($data_type) eq 'enum' || lc($data_type) eq 'set') {
    100 100        
      100        
595 16         39 $field_def .= '(' . $commalist . ')';
596             }
597             elsif (
598             defined $size[0] && $size[0] > 0
599             &&
600             ! grep lc($data_type) eq $_, @no_length_attr
601             ) {
602 174         497 $field_def .= '(' . join( ', ', @size ) . ')';
603             }
604              
605             # char sets
606 311 50       618 $field_def .= " CHARACTER SET $charset" if $charset;
607 311 50       546 $field_def .= " COLLATE $collate" if $collate;
608              
609             # MySQL qualifiers
610 311         590 for my $qual ( qw[ binary unsigned zerofill ] ) {
611 933 100 100     2968 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
612 9         29 $field_def .= " $qual";
613             }
614 311         581 for my $qual ( 'character set', 'collate', 'on update' ) {
615 933 100 66     2761 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
616 24 100       47 if ( ref $val ) {
617 6         20 $field_def .= " $qual ${$val}";
  6         34  
618             }
619             else {
620 18         50 $field_def .= " $qual $val";
621             }
622             }
623              
624             # Null?
625 311 100       5838 if ( $field->is_nullable ) {
626 206         7305 $field_def .= ' NULL';
627             }
628             else {
629 105         4382 $field_def .= ' NOT NULL';
630             }
631              
632             # Default?
633 311         1778 __PACKAGE__->_apply_default_value(
634             $field,
635             \$field_def,
636             [
637             'NULL' => \'NULL',
638             ],
639             );
640              
641 311 100       5255 if ( my $comments = $field->comments ) {
642 13         161 $comments = __PACKAGE__->_quote_string($comments);
643 13         44 $field_def .= qq[ comment $comments];
644             }
645              
646             # auto_increment?
647 311 100       6138 $field_def .= " auto_increment" if $field->is_auto_increment;
648              
649 311         3534 return $field_def;
650             }
651              
652             sub _quote_string {
653 91     91   195 my ($self, $string) = @_;
654              
655 91         369 $string =~ s/([\\'])/$1$1/g;
656 91         301 return qq{'$string'};
657             }
658              
659             sub alter_create_index
660             {
661 4     4 0 27 my ($index, $options) = @_;
662              
663 4         11 my $table_name = _generator($options)->quote($index->table->name);
664 4         23 return join( ' ',
665             'ALTER TABLE',
666             $table_name,
667             'ADD',
668             create_index(@_)
669             );
670             }
671              
672             sub create_index
673             {
674 50     50 1 105 my ( $index, $options ) = @_;
675 50         121 my $generator = _generator($options);
676              
677             return join(
678             ' ',
679 150 100       455 map { $_ || () }
680             lc $index->type eq 'normal' ? 'INDEX' : $index->type . ' INDEX',
681             $index->name
682             ? $generator->quote(truncate_id_uniquely(
683             $index->name,
684             $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH
685             ))
686             : '',
687 50 100 66     902 '(' . join( ', ', map { $generator->quote($_) } $index->fields ) . ')'
  50 100       152  
688             );
689             }
690              
691             sub alter_drop_index
692             {
693 4     4 0 19 my ($index, $options) = @_;
694              
695 4         12 my $table_name = _generator($options)->quote($index->table->name);
696              
697 4   33     77 return join( ' ',
698             'ALTER TABLE',
699             $table_name,
700             'DROP',
701             'INDEX',
702             $index->name || $index->fields
703             );
704              
705             }
706              
707             sub alter_drop_constraint
708             {
709 20     20 0 64 my ($c, $options) = @_;
710              
711 20         45 my $generator = _generator($options);
712 20         381 my $table_name = $generator->quote($c->table->name);
713              
714 20         71 my @out = ('ALTER','TABLE',$table_name,'DROP');
715 20 100       332 if($c->type eq PRIMARY_KEY) {
716 1         32 push @out, $c->type;
717             }
718             else {
719 19 100       594 push @out, ($c->type eq FOREIGN_KEY ? $c->type : "CONSTRAINT"),
720             $generator->quote($c->name);
721             }
722 20         136 return join(' ',@out);
723             }
724              
725             sub alter_create_constraint
726             {
727 20     20 0 71 my ($index, $options) = @_;
728              
729 20         48 my $table_name = _generator($options)->quote($index->table->name);
730 20         72 return join( ' ',
731             'ALTER TABLE',
732             $table_name,
733             'ADD',
734             create_constraint(@_) );
735             }
736              
737             sub create_constraint
738             {
739 154     154 1 337 my ($c, $options) = @_;
740              
741 154         345 my $generator = _generator($options);
742 154   50     617 my $leave_name = $options->{leave_name} || undef;
743              
744 154         733 my $reference_table_name = $generator->quote($c->reference_table);
745              
746 154         537 my @fields = $c->fields;
747              
748 154 100       2687 if ( $c->type eq PRIMARY_KEY ) {
    100          
    100          
    100          
749 47 50       1099 return unless @fields;
750 47         115 return 'PRIMARY KEY (' . join(", ", map { $generator->quote($_) } @fields) . ')';
  59         192  
751             }
752             elsif ( $c->type eq UNIQUE ) {
753 34 50       607 return unless @fields;
754             return sprintf 'UNIQUE %s(%s)',
755             ((defined $c->name && $c->name)
756             ? $generator->quote(
757             truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH ),
758             ) . ' '
759             : ''
760             ),
761 34 100 66     604 ( join ', ', map { $generator->quote($_) } @fields ),
  45   66     129  
762             ;
763             }
764             elsif ( $c->type eq FOREIGN_KEY ) {
765 53 50       1017 return unless @fields;
766             #
767             # Make sure FK field is indexed or MySQL complains.
768             #
769              
770 53         1059 my $table = $c->table;
771 53   66     1651 my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH );
772              
773 53 50       274 my $def = join(' ',
774             'CONSTRAINT',
775             ($c_name ? $generator->quote($c_name) : () ),
776             'FOREIGN KEY'
777             );
778              
779              
780 53         176 $def .= ' ('. join( ', ', map { $generator->quote($_) } @fields ) . ')';
  53         165  
781              
782 53         405 $def .= ' REFERENCES ' . $reference_table_name;
783              
784 53 50       1022 my @rfields = map { $_ || () } $c->reference_fields;
  65         398  
785 53 50       636 unless ( @rfields ) {
786 0         0 my $rtable_name = $c->reference_table;
787 0 0       0 if ( my $ref_table = $table->schema->get_table( $rtable_name ) ) {
788 0         0 push @rfields, $ref_table->primary_key;
789             }
790             else {
791             warn "Can't find reference table '$rtable_name' " .
792 0 0       0 "in schema\n" if $options->{show_warnings};
793             }
794             }
795              
796 53 50       157 if ( @rfields ) {
797 53         134 $def .= ' (' . join( ', ', map { $generator->quote($_) } @rfields ) . ')';
  65         205  
798             }
799             else {
800             warn "FK constraint on " . $table->name . '.' .
801             join('', @fields) . " has no reference fields\n"
802 0 0       0 if $options->{show_warnings};
803             }
804              
805 53 50       1153 if ( $c->match_type ) {
806 0 0       0 $def .= ' MATCH ' .
807             ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
808             }
809              
810 53 100       1897 if ( $c->on_delete ) {
811 1         19 $def .= ' ON DELETE '. $c->on_delete;
812             }
813              
814 53 50       932 if ( $c->on_update ) {
815 0         0 $def .= ' ON UPDATE '. $c->on_update;
816             }
817 53         262 return $def;
818             }
819             elsif ( $c->type eq CHECK_C ) {
820 2         72 my $table = $c->table;
821 2   33     60 my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH );
822              
823 2 50       10 my $def = join(' ',
824             'CONSTRAINT',
825             ($c_name ? $generator->quote($c_name) : () ),
826             'CHECK'
827             );
828              
829              
830 2         37 $def .= ' ('. $c->expression . ')';
831 2         18 return $def;
832             }
833              
834 18         288 return undef;
835             }
836              
837             sub alter_table
838             {
839 5     5 0 25 my ($to_table, $options) = @_;
840              
841 5   50     18 my $table_options = generate_table_options($to_table, $options) || '';
842 5         16 my $table_name = _generator($options)->quote($to_table->name);
843 5         31 my $out = sprintf('ALTER TABLE %s%s',
844             $table_name,
845             $table_options);
846              
847 5         32 return $out;
848             }
849              
850 3     3 0 21 sub rename_field { alter_field(@_) }
851             sub alter_field
852             {
853 25     25 1 128 my ($from_field, $to_field, $options) = @_;
854              
855 25         50 my $generator = _generator($options);
856 25         425 my $table_name = $generator->quote($to_field->table->name);
857              
858 25         489 my $out = sprintf('ALTER TABLE %s CHANGE COLUMN %s %s',
859             $table_name,
860             $generator->quote($from_field->name),
861             create_field($to_field, $options));
862              
863 25         101 return $out;
864             }
865              
866             sub add_field
867             {
868 13     13 1 1496 my ($new_field, $options) = @_;
869              
870 13         29 my $table_name = _generator($options)->quote($new_field->table->name);
871              
872 13         55 my $out = sprintf('ALTER TABLE %s ADD COLUMN %s',
873             $table_name,
874             create_field($new_field, $options));
875              
876 13         51 return $out;
877              
878             }
879              
880             sub drop_field
881             {
882 9     9 1 1307 my ($old_field, $options) = @_;
883              
884 9         26 my $generator = _generator($options);
885 9         179 my $table_name = $generator->quote($old_field->table->name);
886              
887 9         165 my $out = sprintf('ALTER TABLE %s DROP COLUMN %s',
888             $table_name,
889             $generator->quote($old_field->name));
890              
891 9         36 return $out;
892              
893             }
894              
895             sub batch_alter_table {
896 35     35 0 900 my ($table, $diff_hash, $options) = @_;
897              
898             # InnoDB has an issue with dropping and re-adding a FK constraint under the
899             # name in a single alter statement, see: http://bugs.mysql.com/bug.php?id=13741
900             #
901             # We have to work round this.
902              
903 35         65 my %fks_to_alter;
904             my %fks_to_drop = map {
905 18 100       302 $_->type eq FOREIGN_KEY
906             ? ( $_->name => $_ )
907             : ( )
908 35         56 } @{$diff_hash->{alter_drop_constraint} };
  35         92  
909              
910             my %fks_to_create = map {
911 17 100       278 if ( $_->type eq FOREIGN_KEY) {
912 7 100       220 $fks_to_alter{$_->name} = $fks_to_drop{$_->name} if $fks_to_drop{$_->name};
913 7         112 ( $_->name => $_ );
914 10         162 } else { ( ) }
915 35         205 } @{$diff_hash->{alter_create_constraint} };
  35         115  
916              
917 35         49 my @drop_stmt;
918 35 100       103 if (scalar keys %fks_to_alter) {
919             $diff_hash->{alter_drop_constraint} = [
920 1         2 grep { !$fks_to_alter{$_->name} } @{ $diff_hash->{alter_drop_constraint} }
  1         16  
  1         4  
921             ];
922              
923 1         16 @drop_stmt = batch_alter_table($table, { alter_drop_constraint => [ values %fks_to_alter ] }, $options);
924              
925             }
926              
927 35         118 my @stmts = batch_alter_table_statements($diff_hash, $options);
928              
929             #quote
930 35         105 my $generator = _generator($options);
931              
932             # rename_table makes things a bit more complex
933 35         3712 my $renamed_from = "";
934             $renamed_from = $generator->quote($diff_hash->{rename_table}[0][0]->name)
935 35 100 100     143 if $diff_hash->{rename_table} && @{$diff_hash->{rename_table}};
  27         145  
936              
937 35 100       159 return unless @stmts;
938             # Just zero or one stmts. return now
939 17 100       70 return (@drop_stmt,@stmts) unless @stmts > 1;
940              
941             # Now strip off the 'ALTER TABLE xyz' of all but the first one
942              
943 11         213 my $table_name = $generator->quote($table->name);
944              
945 11 100       344 my $re = $renamed_from
946             ? qr/^ALTER TABLE (?:\Q$table_name\E|\Q$renamed_from\E) /
947             : qr/^ALTER TABLE \Q$table_name\E /;
948              
949 11         33 my $first = shift @stmts;
950 11         219 my ($alter_table) = $first =~ /($re)/;
951              
952 11         53 my $padd = " " x length($alter_table);
953              
954 11         30 return @drop_stmt, join( ",\n", $first, map { s/$re//; $padd . $_ } @stmts);
  63         220  
  63         328  
955              
956             }
957              
958             sub drop_table {
959 7     7 0 59 my ($table, $options) = @_;
960              
961             return (
962             # Drop (foreign key) constraints so table drops cleanly
963             batch_alter_table(
964 7         31 $table, { alter_drop_constraint => [ grep { $_->type eq 'FOREIGN KEY' } $table->get_constraints ] }, $options
  8         224  
965             ),
966             'DROP TABLE ' . _generator($options)->quote($table),
967             );
968             }
969              
970             sub rename_table {
971 4     4 0 22 my ($old_table, $new_table, $options) = @_;
972              
973 4         14 my $generator = _generator($options);
974 4         1324 my $old_table_name = $generator->quote($old_table);
975 4         14 my $new_table_name = $generator->quote($new_table);
976              
977 4         20 return "ALTER TABLE $old_table_name RENAME TO $new_table_name";
978             }
979              
980             sub next_unused_name {
981 90   50 90 0 219 my $name = shift || '';
982 90 100       243 if ( !defined($used_names{$name}) ) {
983 80         210 $used_names{$name} = $name;
984 80         1389 return $name;
985             }
986              
987 10         21 my $i = 1;
988 10         48 while ( defined($used_names{$name . '_' . $i}) ) {
989 7         20 ++$i;
990             }
991 10         29 $name .= '_' . $i;
992 10         26 $used_names{$name} = $name;
993 10         166 return $name;
994             }
995              
996             1;
997              
998             =pod
999              
1000             =head1 SEE ALSO
1001              
1002             SQL::Translator, http://www.mysql.com/.
1003              
1004             =head1 AUTHORS
1005              
1006             darren chamberlain Edarren@cpan.orgE,
1007             Ken Youens-Clark Ekclark@cpan.orgE.
1008              
1009             =cut