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