File Coverage

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