File Coverage

blib/lib/SQL/Translator/Producer/MySQL.pm
Criterion Covered Total %
statement 376 395 95.1
branch 163 202 80.6
condition 74 102 72.5
subroutine 32 32 100.0
pod 10 22 45.4
total 655 753 86.9


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 11     11   3490 use strict;
  11         24  
  11         429  
83 11     11   71 use warnings;
  11         26  
  11         1121  
84             our ( $DEBUG, %used_names );
85             our $VERSION = '1.63';
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 11     11   161 use base qw(SQL::Translator::Producer);
  11         36  
  11         2022  
94 11     11   89 use Data::Dumper;
  11         29  
  11         735  
95 11     11   93 use SQL::Translator::Schema::Constants;
  11         37  
  11         962  
96 11     11   5454 use SQL::Translator::Generator::DDL::MySQL;
  11         36  
  11         479  
97 11         58375 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 11     11   576 );
  11         38  
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 47     47 0 132 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 575     575   1217 my ($table, $extra_name, $opt_name) = @_;
152              
153 575         10893 my $extra = $table->extra;
154              
155 575         1112 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 575         10266 my $options = $table->options;
165              
166             # If multiple option names, normalize to the first one
167 575 100       1418 if (ref $opt_name) {
168 291         875 OPT_NAME: for ( @$opt_name[1..$#$opt_name] ) {
169 291         434 for my $idx ( 0..$#{$options} ) {
  291         781  
170 255         386 my ($key, $value) = %{ $options->[$idx] };
  255         885  
171              
172 255 50       908 if (uc $key eq $_) {
173 0         0 $options->[$idx] = { $opt_name->[0] => $value };
174 0         0 last OPT_NAME;
175             }
176             }
177             }
178 291         616 $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 575         822 for my $idx ( 0..$#{$options} ) {
  575         1224  
186 433         617 my ($key, $value) = %{ $options->[$idx] };
  433         1007  
187              
188 433 100       1207 next unless uc $key eq $opt_name;
189              
190             # make sure case is right on option name
191 231         480 delete $options->[$idx]{$key};
192 231   33     1124 return $options->[$idx]{$opt_name} = $value || $extra_type;
193              
194             }
195              
196 344 100       901 if ($extra_type) {
197 4         16 push @$options, { $opt_name => $extra_type };
198 4         14 return $extra_type;
199             }
200              
201 47         385 };
202              
203             # Names are only specific to a given schema
204 47         129 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 47         221 foreach my $table ( $schema->get_tables ) {
211              
212 142         574 $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE'] );
213 142         452 $extra_to_options->($table, 'mysql_charset', 'CHARACTER SET' );
214 142         398 $extra_to_options->($table, 'mysql_collate', 'COLLATE' );
215              
216 142         554 foreach my $c ( $table->get_constraints ) {
217 276 100       8510 next unless $c->type eq FOREIGN_KEY;
218              
219             # Normalize constraint names here.
220 87         3307 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 87 100       468 $c_name = $table->name . '_fk' unless length $c_name;
224              
225 87         447 $c->name( next_unused_name($c_name) );
226              
227 87         238 for my $meth (qw/table reference_table/) {
228 174   100     2194 my $table = $schema->get_table($c->$meth) || next;
229             # This normalizes the types to ENGINE and returns the value if its there
230 149 100       3273 next if $extra_to_options->($table, 'mysql_table_type', ['ENGINE', 'TYPE']);
231 12         281 $table->options( { 'ENGINE' => 'InnoDB' } );
232             }
233             } # foreach constraints
234              
235 142         2120 my %map = ( mysql_collate => 'collate', mysql_charset => 'character set');
236 142         496 foreach my $f ( $table->get_fields ) {
237 459         8846 my $extra = $f->extra;
238 459         1250 for (keys %map) {
239 918 100       2036 $extra->{$map{$_}} = delete $extra->{$_} if exists $extra->{$_};
240             }
241              
242 459         8453 my @size = $f->size;
243 459 100 100     6206 if ( !$size[0] && $f->data_type =~ /char$/ ) {
244 4         77 $f->size( (255) );
245             }
246             }
247              
248             }
249             }
250              
251             {
252             my ($quoting_generator, $nonquoting_generator);
253             sub _generator {
254 748     748   1458 my $options = shift;
255 748 100       2153 return $options->{generator} if exists $options->{generator};
256              
257 287 100 66     792 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 19     19 1 65 my $translator = shift;
267 19         76 local $DEBUG = $translator->debug;
268 19         211 local %used_names;
269 19         407 my $no_comments = $translator->no_comments;
270 19         550 my $add_drop_table = $translator->add_drop_table;
271 19         545 my $schema = $translator->schema;
272 19   100     577 my $show_warnings = $translator->show_warnings || 0;
273 19         572 my $producer_args = $translator->producer_args;
274 19   50     131 my $mysql_version = parse_mysql_version ($producer_args->{mysql_version}, 'perl') || 0;
275 19   33     150 my $max_id_length = $producer_args->{mysql_max_id_length} || $DEFAULT_MAX_ID_LENGTH;
276              
277 19         450 my $generator = _generator({ quote_identifiers => $translator->quote_identifiers });
278              
279 19         135 debug("PKG: Beginning production\n");
280 19         66 %used_names = ();
281 19         51 my $create = '';
282 19 100       68 $create .= header_comment unless ($no_comments);
283             # \todo Don't set if MySQL 3.x is set on command line
284 19         61 my @create = "SET foreign_key_checks=0";
285              
286 19         82 preprocess_schema($schema);
287              
288             #
289             # Generate sql
290             #
291 19         58 my @table_defs =();
292              
293 19         100 for my $table ( $schema->get_tables ) {
294             # print $table->name, "\n";
295 54         468 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 19 50       137 if ($mysql_version >= 5.000001) {
306 0         0 for my $view ( $schema->get_views ) {
307 0         0 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 19 50       87 if ($mysql_version >= 5.000002) {
319 0         0 for my $trigger ( $schema->get_triggers ) {
320 0         0 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 19         77 push @table_defs, "SET foreign_key_checks=1";
334              
335 19 100       141 return wantarray ? ($create ? $create : (), @create, @table_defs) : ($create . join('', map { $_ ? "$_;\n\n" : () } (@create, @table_defs)));
  86 50       2316  
    100          
336             }
337              
338             sub create_trigger {
339 2     2 1 16 my ($trigger, $options) = @_;
340 2         8 my $generator = _generator($options);
341              
342 2         10 my $trigger_name = $trigger->name;
343 2         11 debug("PKG: Looking at trigger '${trigger_name}'\n");
344              
345 2         4 my @statements;
346              
347 2         61 my $events = $trigger->database_events;
348 2         21 for my $event ( @$events ) {
349 2         4 my $name = $trigger_name;
350 2 50       16 if (@$events > 1) {
351 0         0 $name .= "_$event";
352              
353             warn "Multiple database events supplied for trigger '${trigger_name}', ",
354             "creating trigger '${name}' for the '${event}' event\n"
355 0 0       0 if $options->{show_warnings};
356             }
357              
358 2         14 my $action = $trigger->action;
359 2 100       13 if($action !~ /^ \s* BEGIN [\s\;] .*? [\s\;] END [\s\;]* $/six) {
360 1 50       11 $action .= ";" unless $action =~ /;\s*\z/;
361 1         4 $action = "BEGIN $action END";
362             }
363              
364 2 50       6 push @statements, "DROP TRIGGER IF EXISTS " . $generator->quote($name) if $options->{add_drop_trigger};
365 2         9 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 2 50       17 $statements[0] = "--\n-- Trigger " . $generator->quote($trigger_name) . "\n--\n" . $statements[0] unless $options->{no_comments};
374 2         8 return @statements;
375             }
376              
377             sub create_view {
378 4     4 1 159 my ($view, $options) = @_;
379 4         9 my $generator = _generator($options);
380              
381 4         18 my $view_name = $view->name;
382 4         18 my $view_name_qt = $generator->quote($view_name);
383              
384 4         19 debug("PKG: Looking at view '${view_name}'\n");
385              
386             # Header. Should this look like what mysqldump produces?
387 4         25 my $create = '';
388 4 50       16 $create .= "--\n-- View: $view_name_qt\n--\n" unless $options->{no_comments};
389 4         10 $create .= 'CREATE';
390 4 100       13 $create .= ' OR REPLACE' if $options->{add_replace_view};
391 4         12 $create .= "\n";
392              
393 4         89 my $extra = $view->extra;
394             # ALGORITHM
395 4 100 66     29 if( exists($extra->{mysql_algorithm}) && defined(my $algorithm = $extra->{mysql_algorithm}) ){
396 2 50       17 $create .= " ALGORITHM = ${algorithm}\n" if $algorithm =~ /(?:UNDEFINED|MERGE|TEMPTABLE)/i;
397             }
398             # DEFINER
399 4 100 66     16 if( exists($extra->{mysql_definer}) && defined(my $user = $extra->{mysql_definer}) ){
400 2         6 $create .= " DEFINER = ${user}\n";
401             }
402             # SECURITY
403 4 100 66     23 if( exists($extra->{mysql_security}) && defined(my $security = $extra->{mysql_security}) ){
404 2 50       18 $create .= " SQL SECURITY ${security}\n" if $security =~ /(?:DEFINER|INVOKER)/i;
405             }
406              
407             #Header, cont.
408 4         10 $create .= " VIEW $view_name_qt";
409              
410 4 50       81 if( my @fields = $view->fields ){
411 4         16 my $list = join ', ', map { $generator->quote($_) } @fields;
  8         25  
412 4         15 $create .= " ( ${list} )";
413             }
414 4 50       18 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 4         15 $create .= " AS\n ${sql}\n";
418             }
419             # $create .= "";
420 4         15 return $create;
421             }
422              
423             sub create_table
424             {
425 54     54 1 146 my ($table, $options) = @_;
426 54         137 my $generator = _generator($options);
427              
428 54         1198 my $table_name = $generator->quote($table->name);
429 54         283 debug("PKG: Looking at table '$table_name'\n");
430              
431             #
432             # Header. Should this look like what mysqldump produces?
433             #
434 54         141 my $create = '';
435 54         101 my $drop;
436 54 100       196 $create .= "--\n-- Table: $table_name\n--\n" unless $options->{no_comments};
437 54 100       206 $drop = qq[DROP TABLE IF EXISTS $table_name] if $options->{add_drop_table};
438 54         179 $create .= "CREATE TABLE $table_name (\n";
439              
440             #
441             # Fields
442             #
443 54         92 my @field_defs;
444 54         204 for my $field ( $table->get_fields ) {
445 191         602 push @field_defs, create_field($field, $options);
446             }
447              
448             #
449             # Indices
450             #
451 54         172 my @index_defs;
452             my %indexed_fields;
453 54         227 for my $index ( $table->get_indices ) {
454 43         366 push @index_defs, create_index($index, $options);
455 43         853 $indexed_fields{ $_ } = 1 for $index->fields;
456             }
457              
458             #
459             # Constraints -- need to handle more than just FK. -ky
460             #
461 54         127 my @constraint_defs;
462 54         188 my @constraints = $table->get_constraints;
463 54         488 for my $c ( @constraints ) {
464 119         1768 my $constr = create_constraint($c, $options);
465 119 100       549 push @constraint_defs, $constr if($constr);
466              
467 119 100 100     394 unless ( $indexed_fields{ ($c->fields())[0] } || $c->type ne FOREIGN_KEY ) {
468 29         658 push @index_defs, "INDEX (" . $generator->quote(($c->fields())[0]) . ")";
469 29         257 $indexed_fields{ ($c->fields())[0] } = 1;
470             }
471             }
472              
473 54         1013 $create .= join(",\n", map { " $_" }
  364         972  
474             @field_defs, @index_defs, @constraint_defs
475             );
476              
477             #
478             # Footer
479             #
480 54         152 $create .= "\n)";
481 54   100     175 $create .= generate_table_options($table, $options) || '';
482             # $create .= ";\n\n";
483              
484 54 100       541 return $drop ? ($drop,$create) : $create;
485             }
486              
487             sub generate_table_options
488             {
489 59     59 0 145 my ($table, $options) = @_;
490 59         97 my $create;
491              
492 59         110 my $table_type_defined = 0;
493 59         154 my $generator = _generator($options);
494 59         1316 my $charset = $table->extra('mysql_charset');
495 59         1171 my $collate = $table->extra('mysql_collate');
496 59         159 my $union = undef;
497 59         1171 for my $t1_option_ref ( $table->options ) {
498 53         101 my($key, $value) = %{$t1_option_ref};
  53         224  
499 53 100 66     286 $table_type_defined = 1
500             if uc $key eq 'ENGINE' or uc $key eq 'TYPE';
501 53 100       257 if (uc $key eq 'CHARACTER SET') {
    100          
    50          
502 6         9 $charset = $value;
503 6         14 next;
504             } elsif (uc $key eq 'COLLATE') {
505 6         11 $collate = $value;
506 6         13 next;
507             } elsif (uc $key eq 'UNION') {
508 0         0 $union = '(' . join(', ', map { $generator->quote($_) } @$value) . ')';
  0         0  
509 0         0 next;
510             }
511 41         185 $create .= " $key=$value";
512             }
513              
514 59         1153 my $mysql_table_type = $table->extra('mysql_table_type');
515 59 50 33     226 $create .= " ENGINE=$mysql_table_type"
516             if $mysql_table_type && !$table_type_defined;
517 59         1195 my $comments = $table->comments;
518              
519 59 100       217 $create .= " DEFAULT CHARACTER SET $charset" if $charset;
520 59 100       159 $create .= " COLLATE $collate" if $collate;
521 59 50       157 $create .= " UNION=$union" if $union;
522 59 100       144 $create .= qq[ comment='$comments'] if $comments;
523 59         331 return $create;
524             }
525              
526             sub create_field
527             {
528 278     278 1 5162 my ($field, $options) = @_;
529              
530 278         652 my $generator = _generator($options);
531              
532 278         5824 my $field_name = $field->name;
533 278         6537 debug("PKG: Looking at field '$field_name'\n");
534 278         1084 my $field_def = $generator->quote($field_name);
535              
536             # data type and size
537 278         1030 my $data_type = $field->data_type;
538 278         5505 my @size = $field->size;
539 278         7268 my %extra = $field->extra;
540 278   100     1370 my $list = $extra{'list'} || [];
541 278         759 my $commalist = join( ', ', map { __PACKAGE__->_quote_string($_) } @$list );
  36         84  
542 278         513 my $charset = $extra{'mysql_charset'};
543 278         441 my $collate = $extra{'mysql_collate'};
544              
545 278   100     993 my $mysql_version = $options->{mysql_version} || 0;
546             #
547             # Oracle "number" type -- figure best MySQL type
548             #
549 278 100 100     2010 if ( lc $data_type eq 'number' ) {
    100          
    100          
    100          
550             # not an integer
551 8 100 66     58 if ( scalar @size > 1 ) {
    100 66        
    100          
552 2         3 $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         4 $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 21 100 100     144 unless ($size[0] <= 65535 && $mysql_version >= 5.000003 ) {
570 17         53 $data_type = 'text';
571 17         42 @size = ();
572             }
573             }
574             elsif ( $data_type =~ /boolean/i ) {
575 6 100       28 if ($mysql_version >= 4) {
576 2         4 $data_type = 'boolean';
577             } else {
578 4         15 $data_type = 'enum';
579 4         9 $commalist = "'0','1'";
580             }
581             }
582             elsif ( exists $translate{ lc $data_type } ) {
583 102         306 $data_type = $translate{ lc $data_type };
584             }
585              
586 278 100       1283 @size = () if $data_type =~ /(text|blob)/i;
587              
588 278 50 66     1133 if ( $data_type =~ /(double|float)/ && scalar @size == 1 ) {
589 0         0 push @size, '0';
590             }
591              
592 278         749 $field_def .= " $data_type";
593              
594 278 100 100     2335 if ( lc($data_type) eq 'enum' || lc($data_type) eq 'set') {
    100 100        
      100        
595 16         33 $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 148         459 $field_def .= '(' . join( ', ', @size ) . ')';
603             }
604              
605             # char sets
606 278 50       633 $field_def .= " CHARACTER SET $charset" if $charset;
607 278 50       564 $field_def .= " COLLATE $collate" if $collate;
608              
609             # MySQL qualifiers
610 278         637 for my $qual ( qw[ binary unsigned zerofill ] ) {
611 834 100 100     3199 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
612 6         39 $field_def .= " $qual";
613             }
614 278         522 for my $qual ( 'character set', 'collate', 'on update' ) {
615 834 100 66     2949 my $val = $extra{ $qual } || $extra{ uc $qual } or next;
616 24 100       53 if ( ref $val ) {
617 6         23 $field_def .= " $qual ${$val}";
  6         20  
618             }
619             else {
620 18         53 $field_def .= " $qual $val";
621             }
622             }
623              
624             # Null?
625 278 100       6399 if ( $field->is_nullable ) {
626 182         7483 $field_def .= ' NULL';
627             }
628             else {
629 96         4767 $field_def .= ' NOT NULL';
630             }
631              
632             # Default?
633 278         1605 __PACKAGE__->_apply_default_value(
634             $field,
635             \$field_def,
636             [
637             'NULL' => \'NULL',
638             ],
639             );
640              
641 278 100       5695 if ( my $comments = $field->comments ) {
642 10         146 $comments = __PACKAGE__->_quote_string($comments);
643 10         51 $field_def .= qq[ comment $comments];
644             }
645              
646             # auto_increment?
647 278 100       6446 $field_def .= " auto_increment" if $field->is_auto_increment;
648              
649 278         3715 return $field_def;
650             }
651              
652             sub _quote_string {
653 76     76   180 my ($self, $string) = @_;
654              
655 76         354 $string =~ s/([\\'])/$1$1/g;
656 76         277 return qq{'$string'};
657             }
658              
659             sub alter_create_index
660             {
661 4     4 0 60 my ($index, $options) = @_;
662              
663 4         24 my $table_name = _generator($options)->quote($index->table->name);
664 4         56 return join( ' ',
665             'ALTER TABLE',
666             $table_name,
667             'ADD',
668             create_index(@_)
669             );
670             }
671              
672             sub create_index
673             {
674 47     47 1 127 my ( $index, $options ) = @_;
675 47         137 my $generator = _generator($options);
676              
677             return join(
678             ' ',
679 141 100       572 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 47 100 66     1062 '(' . join( ', ', map { $generator->quote($_) } $index->fields ) . ')'
  47 100       149  
688             );
689             }
690              
691             sub alter_drop_index
692             {
693 4     4 0 32 my ($index, $options) = @_;
694              
695 4         25 my $table_name = _generator($options)->quote($index->table->name);
696              
697 4   33     93 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 92 my ($c, $options) = @_;
710              
711 20         53 my $generator = _generator($options);
712 20         461 my $table_name = $generator->quote($c->table->name);
713              
714 20         87 my @out = ('ALTER','TABLE',$table_name,'DROP');
715 20 100       414 if($c->type eq PRIMARY_KEY) {
716 1         56 push @out, $c->type;
717             }
718             else {
719 19 100       700 push @out, ($c->type eq FOREIGN_KEY ? $c->type : "CONSTRAINT"),
720             $generator->quote($c->name);
721             }
722 20         178 return join(' ',@out);
723             }
724              
725             sub alter_create_constraint
726             {
727 20     20 0 91 my ($index, $options) = @_;
728              
729 20         82 my $table_name = _generator($options)->quote($index->table->name);
730 20         90 return join( ' ',
731             'ALTER TABLE',
732             $table_name,
733             'ADD',
734             create_constraint(@_) );
735             }
736              
737             sub create_constraint
738             {
739 139     139 1 309 my ($c, $options) = @_;
740              
741 139         309 my $generator = _generator($options);
742 139   50     615 my $leave_name = $options->{leave_name} || undef;
743              
744 139         626 my $reference_table_name = $generator->quote($c->reference_table);
745              
746 139         502 my @fields = $c->fields;
747              
748 139 100       2811 if ( $c->type eq PRIMARY_KEY ) {
    100          
    100          
    100          
749 41 50       883 return unless @fields;
750 41         113 return 'PRIMARY KEY (' . join(", ", map { $generator->quote($_) } @fields) . ')';
  53         185  
751             }
752             elsif ( $c->type eq UNIQUE ) {
753 28 50       619 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 28 100 66     580 ( join ', ', map { $generator->quote($_) } @fields ),
  39   66     159  
762             ;
763             }
764             elsif ( $c->type eq FOREIGN_KEY ) {
765 50 50       1071 return unless @fields;
766             #
767             # Make sure FK field is indexed or MySQL complains.
768             #
769              
770 50         996 my $table = $c->table;
771 50   66     1853 my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH );
772              
773 50 50       241 my $def = join(' ',
774             'CONSTRAINT',
775             ($c_name ? $generator->quote($c_name) : () ),
776             'FOREIGN KEY'
777             );
778              
779              
780 50         389 $def .= ' ('. join( ', ', map { $generator->quote($_) } @fields ) . ')';
  50         150  
781              
782 50         433 $def .= ' REFERENCES ' . $reference_table_name;
783              
784 50 50       1071 my @rfields = map { $_ || () } $c->reference_fields;
  62         458  
785 50 50       629 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 50 50       135 if ( @rfields ) {
797 50         119 $def .= ' (' . join( ', ', map { $generator->quote($_) } @rfields ) . ')';
  62         209  
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 50 50       1266 if ( $c->match_type ) {
806 0 0       0 $def .= ' MATCH ' .
807             ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
808             }
809              
810 50 100       2078 if ( $c->on_delete ) {
811 1         24 $def .= ' ON DELETE '. $c->on_delete;
812             }
813              
814 50 50       945 if ( $c->on_update ) {
815 0         0 $def .= ' ON UPDATE '. $c->on_update;
816             }
817 50         257 return $def;
818             }
819             elsif ( $c->type eq CHECK_C ) {
820 2         112 my $table = $c->table;
821 2   33     76 my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH );
822              
823 2 50       24 my $def = join(' ',
824             'CONSTRAINT',
825             ($c_name ? $generator->quote($c_name) : () ),
826             'CHECK'
827             );
828              
829              
830 2         59 $def .= ' ('. $c->expression . ')';
831 2         35 return $def;
832             }
833              
834 18         341 return undef;
835             }
836              
837             sub alter_table
838             {
839 5     5 0 43 my ($to_table, $options) = @_;
840              
841 5   50     33 my $table_options = generate_table_options($to_table, $options) || '';
842 5         22 my $table_name = _generator($options)->quote($to_table->name);
843 5         35 my $out = sprintf('ALTER TABLE %s%s',
844             $table_name,
845             $table_options);
846              
847 5         36 return $out;
848             }
849              
850 3     3 0 34 sub rename_field { alter_field(@_) }
851             sub alter_field
852             {
853 25     25 1 199 my ($from_field, $to_field, $options) = @_;
854              
855 25         54 my $generator = _generator($options);
856 25         560 my $table_name = $generator->quote($to_field->table->name);
857              
858 25         520 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         107 return $out;
864             }
865              
866             sub add_field
867             {
868 13     13 1 1644 my ($new_field, $options) = @_;
869              
870 13         58 my $table_name = _generator($options)->quote($new_field->table->name);
871              
872 13         131 my $out = sprintf('ALTER TABLE %s ADD COLUMN %s',
873             $table_name,
874             create_field($new_field, $options));
875              
876 13         62 return $out;
877              
878             }
879              
880             sub drop_field
881             {
882 9     9 1 1596 my ($old_field, $options) = @_;
883              
884 9         32 my $generator = _generator($options);
885 9         274 my $table_name = $generator->quote($old_field->table->name);
886              
887 9         214 my $out = sprintf('ALTER TABLE %s DROP COLUMN %s',
888             $table_name,
889             $generator->quote($old_field->name));
890              
891 9         52 return $out;
892              
893             }
894              
895             sub batch_alter_table {
896 35     35 0 1045 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         72 my %fks_to_alter;
904             my %fks_to_drop = map {
905 18 100       351 $_->type eq FOREIGN_KEY
906             ? ( $_->name => $_ )
907             : ( )
908 35         70 } @{$diff_hash->{alter_drop_constraint} };
  35         108  
909              
910             my %fks_to_create = map {
911 17 100       385 if ( $_->type eq FOREIGN_KEY) {
912 7 100       311 $fks_to_alter{$_->name} = $fks_to_drop{$_->name} if $fks_to_drop{$_->name};
913 7         137 ( $_->name => $_ );
914 10         200 } else { ( ) }
915 35         210 } @{$diff_hash->{alter_create_constraint} };
  35         98  
916              
917 35         71 my @drop_stmt;
918 35 100       104 if (scalar keys %fks_to_alter) {
919             $diff_hash->{alter_drop_constraint} = [
920 1         20 grep { !$fks_to_alter{$_->name} } @{ $diff_hash->{alter_drop_constraint} }
  1         23  
  1         5  
921             ];
922              
923 1         13 @drop_stmt = batch_alter_table($table, { alter_drop_constraint => [ values %fks_to_alter ] }, $options);
924              
925             }
926              
927 35         135 my @stmts = batch_alter_table_statements($diff_hash, $options);
928              
929             #quote
930 35         127 my $generator = _generator($options);
931              
932             # rename_table makes things a bit more complex
933 35         4387 my $renamed_from = "";
934             $renamed_from = $generator->quote($diff_hash->{rename_table}[0][0]->name)
935 35 100 100     172 if $diff_hash->{rename_table} && @{$diff_hash->{rename_table}};
  27         172  
936              
937 35 100       216 return unless @stmts;
938             # Just zero or one stmts. return now
939 17 100       107 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         294 my $table_name = $generator->quote($table->name);
944              
945 11 100       376 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         35 my $first = shift @stmts;
950 11         256 my ($alter_table) = $first =~ /($re)/;
951              
952 11         54 my $padd = " " x length($alter_table);
953              
954 11         35 return @drop_stmt, join( ",\n", $first, map { s/$re//; $padd . $_ } @stmts);
  63         230  
  63         349  
955              
956             }
957              
958             sub drop_table {
959 7     7 0 135 my ($table, $options) = @_;
960              
961             return (
962             # Drop (foreign key) constraints so table drops cleanly
963             batch_alter_table(
964 7         36 $table, { alter_drop_constraint => [ grep { $_->type eq 'FOREIGN KEY' } $table->get_constraints ] }, $options
  8         280  
965             ),
966             'DROP TABLE ' . _generator($options)->quote($table),
967             );
968             }
969              
970             sub rename_table {
971 4     4 0 23 my ($old_table, $new_table, $options) = @_;
972              
973 4         11 my $generator = _generator($options);
974 4         1984 my $old_table_name = $generator->quote($old_table);
975 4         14 my $new_table_name = $generator->quote($new_table);
976              
977 4         18 return "ALTER TABLE $old_table_name RENAME TO $new_table_name";
978             }
979              
980             sub next_unused_name {
981 87   50 87 0 263 my $name = shift || '';
982 87 100       284 if ( !defined($used_names{$name}) ) {
983 77         239 $used_names{$name} = $name;
984 77         1531 return $name;
985             }
986              
987 10         24 my $i = 1;
988 10         41 while ( defined($used_names{$name . '_' . $i}) ) {
989 7         27 ++$i;
990             }
991 10         23 $name .= '_' . $i;
992 10         28 $used_names{$name} = $name;
993 10         195 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