File Coverage

script/sqlt-diff-old
Criterion Covered Total %
statement 207 251 82.4
branch 117 218 53.6
condition 21 41 51.2
subroutine 9 9 100.0
pod n/a
total 354 519 68.2


line stmt bran cond sub pod time code
1             #!perl
2             # vim: set ft=perl:
3              
4             # -------------------------------------------------------------------
5             # Copyright (C) 2002-2009 The SQLFairy Authors
6             #
7             # This program is free software; you can redistribute it and/or
8             # modify it under the terms of the GNU General Public License as
9             # published by the Free Software Foundation; version 2.
10             #
11             # This program is distributed in the hope that it will be useful, but
12             # WITHOUT ANY WARRANTY; without even the implied warranty of
13             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14             # General Public License for more details.
15             #
16             # You should have received a copy of the GNU General Public License
17             # along with this program; if not, write to the Free Software
18             # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19             # 02110-1301 USA.
20             # -------------------------------------------------------------------
21              
22             =head1 NAME
23              
24             sqlt-diff - find the differences b/w two schemas
25              
26             =head1 SYNOPSIS
27              
28             For help:
29              
30             sqlt-diff -h|--help
31              
32             For a list of all valid parsers:
33              
34             sqlt -l|--list
35              
36             To diff two schemas:
37              
38             sqlt-diff [options] file_name1=parser file_name2=parser
39              
40             Options:
41              
42             -d|--debug Show debugging info
43              
44             =head1 DESCRIPTION
45              
46             sqlt-diff is a utility for creating a file of SQL commands necessary to
47             transform the first schema provided to the second. While not yet
48             exhaustive in its ability to mutate the entire schema, it will report the
49             following
50              
51             =over
52              
53             =item * New tables
54              
55             Using the Producer class of the target (second) schema, any tables missing
56             in the first schema will be generated in their entirety (fields, constraints,
57             indices).
58              
59             =item * Missing/altered fields
60              
61             Any fields missing or altered between the two schemas will be reported
62             as:
63              
64             ALTER TABLE
65             [DROP ]
66             [CHANGE ()] ;
67              
68             =item * Missing/altered indices
69              
70             Any indices missing or of a different type or on different fields will be
71             indicated. Indices that should be dropped will be reported as such:
72              
73             DROP INDEX ON ;
74              
75             An index of a different type or on different fields will be reported as a
76             new index as such:
77              
78             CREATE [] INDEX [] ON
79             ( [,] ) ;
80              
81             =back
82              
83             "ALTER/DROP TABLE" and "CREATE INDEX" statements B generated by
84             the Producer, unfortunately, and may require massaging before being passed to
85             your target database.
86              
87             =cut
88              
89             # -------------------------------------------------------------------
90              
91 5     5   26 use strict;
  5         8  
  5         158  
92 5     5   25 use warnings;
  5         6  
  5         253  
93 5     5   3015 use Pod::Usage;
  5         472638  
  5         785  
94 5     5   3543 use Data::Dumper;
  5         45980  
  5         476  
95 5     5   3655 use SQL::Translator;
  5         25  
  5         205  
96 5     5   38 use SQL::Translator::Schema::Constants;
  5         11  
  5         402  
97              
98 5     5   28 use vars qw( $VERSION );
  5         11  
  5         32629  
99 5         1030384 $VERSION = '1.66';
100              
101 5         16 my (@input, $list, $help, $debug);
102 5         28 for my $arg (@ARGV) {
103 10 50       127 if ($arg =~ m/^-?-l(ist)?$/) {
    50          
    50          
    50          
104 0         0 $list = 1;
105             } elsif ($arg =~ m/^-?-h(elp)?$/) {
106 0         0 $help = 1;
107             } elsif ($arg =~ m/^-?-d(ebug)?$/) {
108 0         0 $debug = 1;
109             } elsif ($arg =~ m/^([^=]+)=(.+)$/) {
110 10         74 push @input, { file => $1, parser => $2 };
111             } else {
112 0         0 pod2usage(msg => "Unknown argument '$arg'");
113             }
114             }
115              
116 5 50       25 pod2usage(1) if $help;
117 5 50       23 pod2usage('Please specify only two schemas to diff') if scalar @input > 2;
118 5 50       24 pod2usage('No input') if !@input;
119              
120 5 50 33     68 if (my $interactive = -t STDIN && -t STDOUT) {
121 0         0 print STDERR join("\n",
122             "sqlt-diff-old is deprecated. Please sqlt-diff, and tell us ",
123             "about any problems or patch SQL::Translator::Diff",
124             '',
125             );
126             }
127              
128 5         340 my $tr = SQL::Translator->new;
129 5         185 my @parsers = $tr->list_parsers;
130 5         19 my %valid_parsers = map { $_, 1 } @parsers;
  120         200  
131              
132 5 50       29 if ($list) {
133 0         0 print "\nParsers:\n", map {"\t$_\n"} sort @parsers;
  0         0  
134 0         0 print "\n";
135 0         0 exit(0);
136             }
137              
138 5 50       19 pod2usage(msg => 'Too many file args') if @input > 2;
139              
140 5         18 my ($source_schema, $source_db, $target_schema, $target_db);
141              
142 5         11 my $i = 2;
143 5         15 for my $in (@input) {
144 10         58 my $file = $in->{'file'};
145 10         28 my $parser = $in->{'parser'};
146              
147 10 50       447 die "Unable to read file '$file'\n" unless -r $file;
148 10 50       43 die "'$parser' is an invalid parser\n" unless $valid_parsers{$parser};
149              
150 10         354 my $t = SQL::Translator->new;
151 10         244 $t->debug($debug);
152 10 50       268 $t->parser($parser) or die $tr->error;
153 10 50       52 my $out = $t->translate($file) or die $tr->error;
154 10         186 my $schema = $t->schema;
155 10 50       108 unless ($schema->name) {
156 10         44 $schema->name($file);
157             }
158              
159 10 100       33 if ($i == 1) {
160 5         14 $source_schema = $schema;
161 5         19 $source_db = $parser;
162             } else {
163 5         1660 $target_schema = $schema;
164 5         16 $target_db = $parser;
165             }
166 10         220 $i--;
167             }
168 5         23 my $case_insensitive = $target_db =~ /SQLServer/;
169              
170 5         22 my $s1_name = $source_schema->name;
171 5         26 my $s2_name = $target_schema->name;
172 5         33 my (@new_tables, @diffs, @diffs_at_end);
173 5         36 for my $t1 ($source_schema->get_tables) {
174 11         299 my $t1_name = $t1->name;
175 11         344 my $t2 = $target_schema->get_table($t1_name, $case_insensitive);
176              
177 11 50       54 warn "TABLE '$s1_name.$t1_name'\n" if $debug;
178 11 100       350 unless ($t2) {
179 1 50       5 warn "Couldn't find table '$s1_name.$t1_name' in '$s2_name'\n"
180             if $debug;
181 1 50       5 if ($target_db =~ /(SQLServer|Oracle)/) {
182 0         0 for my $constraint ($t1->get_constraints) {
183 0 0       0 next if $constraint->type ne FOREIGN_KEY;
184 0         0 push @diffs_at_end, "ALTER TABLE $t1_name ADD " . constraint_to_string($constraint, $source_schema) . ";";
185 0         0 $t1->drop_constraint($constraint);
186             }
187             }
188 1         4 push @new_tables, $t1;
189 1         5 next;
190             }
191              
192             # Go through our options
193 10         229 my $options_different = 0;
194 10         41 my %checkedOptions;
195             OPTION:
196 10         246 for my $t1_option_ref ($t1->options) {
197 4         8 my ($key1, $value1) = %{$t1_option_ref};
  4         18  
198 4         103 for my $t2_option_ref ($t2->options) {
199 4         11 my ($key2, $value2) = %{$t2_option_ref};
  4         17  
200 4 50       17 if ($key1 eq $key2) {
201 4 50       15 if (defined $value1 != defined $value2) {
202 0         0 $options_different = 1;
203 0         0 last OPTION;
204             }
205 4 100 66     26 if (defined $value1 && $value1 ne $value2) {
206 1         3 $options_different = 1;
207 1         4 last OPTION;
208             }
209 3         11 $checkedOptions{$key1} = 1;
210 3         12 next OPTION;
211             }
212             }
213 0         0 $options_different = 1;
214 0         0 last OPTION;
215             }
216              
217             # Go through the other table's options
218 10 100       59 unless ($options_different) {
219 9         216 for my $t2_option_ref ($t2->options) {
220 3         7 my ($key, $value) = %{$t2_option_ref};
  3         9  
221 3 50       15 next if $checkedOptions{$key};
222 0         0 $options_different = 1;
223 0         0 last;
224             }
225             }
226              
227             # If there's a difference, just re-set all the options
228 10         57 my @diffs_table_options;
229 10 100       39 if ($options_different) {
230 1         3 my @options = ();
231 1         28 foreach my $option_ref ($t1->options) {
232 1         3 my ($key, $value) = %{$option_ref};
  1         4  
233 1 50       7 push(@options, defined $value ? "$key=$value" : $key);
234             }
235 1         4 my $options = join(' ', @options);
236 1         4 @diffs_table_options = ("ALTER TABLE $t1_name $options;");
237             }
238              
239 10         237 my $t2_name = $t2->name;
240 10         243 my (@diffs_table_adds, @diffs_table_changes);
241 10         56 for my $t1_field ($t1->get_fields) {
242 42         185 my $f1_type = $t1_field->data_type;
243 42         1135 my $f1_size = $t1_field->size;
244 42         1312 my $f1_name = $t1_field->name;
245 42         1876 my $f1_nullable = $t1_field->is_nullable;
246 42         2824 my $f1_default = $t1_field->default_value;
247 42         754 my $f1_auto_inc = $t1_field->is_auto_increment;
248 42         658 my $t2_field = $t2->get_field($f1_name, $case_insensitive);
249 42         343 my $f1_full_name = "$s1_name.$t1_name.$t1_name";
250 42 50       127 warn "FIELD '$f1_full_name'\n" if $debug;
251              
252 42         122 my $f2_full_name = "$s2_name.$t2_name.$f1_name";
253              
254 42 100       422 unless ($t2_field) {
255 3 50       12 warn "Couldn't find field '$f2_full_name' in '$t2_name'\n"
256             if $debug;
257 3         7 my $temp_default_value = 0;
258 3 0 33     16 if ( $target_db =~ /SQLServer/
      33        
259             && !$f1_nullable
260             && !defined $f1_default) {
261             # SQL Server doesn't allow adding non-nullable, non-default columns
262             # so we add it with a default value, then remove the default value
263 0         0 $temp_default_value = 1;
264 0         0 my (@numeric_types) = qw(decimal numeric float real int bigint smallint tinyint);
265 0 0       0 $f1_default = grep($_ eq $f1_type, @numeric_types) ? 0 : '';
266             }
267 3 50 100     81 push @diffs_table_adds,
    100          
    50          
    50          
    100          
    50          
    50          
    50          
268             sprintf(
269             "ALTER TABLE %s ADD %s%s %s%s%s%s%s%s;",
270             $t1_name,
271             $target_db =~ /Oracle/ ? '(' : '',
272             $f1_name,
273             $f1_type,
274             ($f1_size && $f1_type !~ /(blob|text)$/) ? "($f1_size)" : '',
275             !defined $f1_default ? ''
276             : uc $f1_default eq 'NULL' ? ' DEFAULT NULL'
277             : uc $f1_default eq 'CURRENT_TIMESTAMP' ? ' DEFAULT CURRENT_TIMESTAMP'
278             : " DEFAULT '$f1_default'",
279             $f1_nullable ? '' : ' NOT NULL',
280             $f1_auto_inc ? ' AUTO_INCREMENT' : '',
281             $target_db =~ /Oracle/ ? ')' : '',
282             );
283 3 50       13 if ($temp_default_value) {
284 0         0 undef $f1_default;
285 0         0 push @diffs_table_adds, sprintf(
286             <
287             DECLARE \@defname VARCHAR(100), \@cmd VARCHAR(1000)
288             SET \@defname =
289             (SELECT name
290             FROM sysobjects so JOIN sysconstraints sc
291             ON so.id = sc.constid
292             WHERE object_name(so.parent_obj) = '%s'
293             AND so.xtype = 'D'
294             AND sc.colid =
295             (SELECT colid FROM syscolumns
296             WHERE id = object_id('%s') AND
297             name = '%s'))
298             SET \@cmd = 'ALTER TABLE %s DROP CONSTRAINT '
299             + \@defname
300             EXEC(\@cmd)
301             END
302             , $t1_name, $t1_name, $f1_name, $t1_name,
303             );
304             }
305 3         11 next;
306             }
307              
308 39         928 my $f2_type = $t2_field->data_type;
309 39   100     981 my $f2_size = $t2_field->size || '';
310 39         1575 my $f2_nullable = $t2_field->is_nullable;
311 39         2505 my $f2_default = $t2_field->default_value;
312 39         688 my $f2_auto_inc = $t2_field->is_auto_increment;
313 39 100       1268 if (!$t1_field->equals($t2_field, $case_insensitive)) {
314              
315             # SQLServer timestamp fields can't be altered, so we drop and add instead
316 6 50 33     107 if ($target_db =~ /SQLServer/ && $f2_type eq "timestamp") {
317 0         0 push @diffs_table_changes, "ALTER TABLE $t1_name DROP COLUMN $f1_name;";
318 0 0 0     0 push @diffs_table_changes,
    0          
    0          
    0          
    0          
    0          
    0          
    0          
319             sprintf(
320             "ALTER TABLE %s ADD %s%s %s%s%s%s%s%s;",
321             $t1_name,
322             $target_db =~ /Oracle/ ? '(' : '',
323             $f1_name,
324             $f1_type,
325             ($f1_size && $f1_type !~ /(blob|text)$/) ? "($f1_size)"
326             : '',
327             !defined $f1_default ? ''
328             : uc $f1_default eq 'NULL' ? ' DEFAULT NULL'
329             : uc $f1_default eq 'CURRENT_TIMESTAMP' ? ' DEFAULT CURRENT_TIMESTAMP'
330             : " DEFAULT '$f1_default'",
331             $f1_nullable ? '' : ' NOT NULL',
332             $f1_auto_inc ? ' AUTO_INCREMENT' : '',
333             $target_db =~ /Oracle/ ? ')' : '',
334             );
335 0         0 next;
336             }
337              
338 6 50       33 my $changeText
    50          
339             = $target_db =~ /SQLServer/ ? 'ALTER COLUMN'
340             : $target_db =~ /Oracle/ ? 'MODIFY ('
341             : 'CHANGE';
342 6 100       20 my $nullText = $f1_nullable ? '' : ' NOT NULL';
343 6 50 33     25 $nullText = ''
344             if $target_db =~ /Oracle/ && $f1_nullable == $f2_nullable;
345 6 100 66     493 push @diffs_table_changes,
    100 66        
    50          
    50          
    100          
    100          
    50          
346             sprintf(
347             "ALTER TABLE %s %s %s%s %s%s%s%s%s%s;",
348             $t1_name,
349             $changeText,
350             $f1_name,
351             $target_db =~ /MySQL/ ? " $f1_name" : '',
352             $f1_type,
353             ($f1_size && $f1_type !~ /(blob|text)$/) ? "($f1_size)" : '',
354             $nullText,
355             !defined $f1_default || $target_db =~ /SQLServer/ ? ''
356             : uc $f1_default eq 'NULL' ? ' DEFAULT NULL'
357             : uc $f1_default eq 'CURRENT_TIMESTAMP' ? ' DEFAULT CURRENT_TIMESTAMP'
358             : " DEFAULT '$f1_default'",
359             $f1_auto_inc ? ' AUTO_INCREMENT' : '',
360             $target_db =~ /Oracle/ ? ')' : '',
361             );
362 6 50 66     40 if (defined $f1_default && $target_db =~ /SQLServer/) {
363              
364             # Adding a column with a default value for SQL Server means adding a
365             # constraint and setting existing NULLs to the default value
366 0 0       0 push @diffs_table_changes,
    0          
367             sprintf(
368             "ALTER TABLE %s ADD CONSTRAINT DF_%s_%s %s FOR %s;",
369             $t1_name,
370             $t1_name,
371             $f1_name,
372             uc $f1_default eq 'NULL' ? 'DEFAULT NULL'
373             : uc $f1_default eq 'CURRENT_TIMESTAMP' ? 'DEFAULT CURRENT_TIMESTAMP'
374             : "DEFAULT '$f1_default'",
375             $f1_name,
376             );
377 0 0       0 push @diffs_table_changes,
    0          
378             sprintf(
379             "UPDATE %s SET %s = %s WHERE %s IS NULL;",
380             $t1_name,
381             $f1_name,
382             uc $f1_default eq 'NULL' ? 'NULL'
383             : uc $f1_default eq 'CURRENT_TIMESTAMP' ? 'CURRENT_TIMESTAMP'
384             : "'$f1_default'",
385             $f1_name,
386             );
387             }
388             }
389             }
390              
391 10         40 my (%checked_indices, @diffs_index_creates, @diffs_index_drops);
392             INDEX:
393 10         64 for my $i1 ($t1->get_indices) {
394 2         28 for my $i2 ($t2->get_indices) {
395 2 100       58 if ($i1->equals($i2, $case_insensitive)) {
396 1         4 $checked_indices{$i2} = 1;
397 1         4 next INDEX;
398             }
399             }
400 1 50       61 push @diffs_index_creates,
    50          
401             sprintf(
402             "CREATE %sINDEX%s ON %s (%s);",
403             $i1->type eq NORMAL ? '' : $i1->type . " ",
404             $i1->name ? " " . $i1->name : '',
405             $t1_name, join(",", $i1->fields),
406             );
407             }
408             INDEX2:
409 10         44 for my $i2 ($t2->get_indices) {
410 2 100       25 next if $checked_indices{$i2};
411 1         5 for my $i1 ($t1->get_indices) {
412 1 50       238 next INDEX2 if $i2->equals($i1, $case_insensitive);
413             }
414 1 50       37 $target_db =~ /SQLServer/
415             ? push @diffs_index_drops, "DROP INDEX $t1_name." . $i2->name . ";"
416             : push @diffs_index_drops,
417             "DROP INDEX " . $i2->name . " on $t1_name;";
418             }
419              
420 10         35 my (%checked_constraints, @diffs_constraint_drops);
421             CONSTRAINT:
422 10         41 for my $c1 ($t1->get_constraints) {
423             next
424 22 50 66     241 if $source_db =~ /Oracle/
      33        
425             && $c1->type eq UNIQUE
426             && $c1->name =~ /^SYS_/i;
427 22         158 for my $c2 ($t2->get_constraints) {
428 42 100       1564 if ($c1->equals($c2, $case_insensitive)) {
429 17         77 $checked_constraints{$c2} = 1;
430 17         74 next CONSTRAINT;
431             }
432             }
433 5         139 push @diffs_at_end, "ALTER TABLE $t1_name ADD " . constraint_to_string($c1, $source_schema) . ";";
434             }
435             CONSTRAINT2:
436 10         58 for my $c2 ($t2->get_constraints) {
437 22 100       207 next if $checked_constraints{$c2};
438 5         23 for my $c1 ($t1->get_constraints) {
439 13 50       427 next CONSTRAINT2 if $c2->equals($c1, $case_insensitive);
440             }
441 5 100       176 if ($c2->type eq UNIQUE) {
    50          
442 1         54 push @diffs_constraint_drops, "ALTER TABLE $t1_name DROP INDEX " . $c2->name . ";";
443             } elsif ($target_db =~ /SQLServer/) {
444 0         0 push @diffs_constraint_drops, "ALTER TABLE $t1_name DROP " . $c2->name . ";";
445             } else {
446 4 100       182 push @diffs_constraint_drops,
447             "ALTER TABLE $t1_name DROP " . $c2->type . ($c2->type eq FOREIGN_KEY ? " " . $c2->name : '') . ";";
448             }
449             }
450              
451 10         78 push @diffs, @diffs_index_drops, @diffs_constraint_drops,
452             @diffs_table_options, @diffs_table_adds,
453             @diffs_table_changes, @diffs_index_creates;
454             }
455              
456 5         40 for my $t2 ($target_schema->get_tables) {
457 11         390 my $t2_name = $t2->name;
458 11         291 my $t1 = $source_schema->get_table($t2_name, $target_db =~ /SQLServer/);
459              
460 11 100       49 unless ($t1) {
461 1 50       7 if ($target_db =~ /SQLServer/) {
462 0         0 for my $constraint ($t2->get_constraints) {
463 0 0       0 next if $constraint->type eq PRIMARY_KEY;
464 0         0 push @diffs, "ALTER TABLE $t2_name DROP " . $constraint->name . ";";
465             }
466             }
467 1         5 push @diffs_at_end, "DROP TABLE $t2_name;";
468 1         4 next;
469             }
470              
471 10         224 for my $t2_field ($t2->get_fields) {
472 41         1448 my $f2_name = $t2_field->name;
473 41         869 my $t1_field = $t1->get_field($f2_name);
474 41 100       335 unless ($t1_field) {
475 2 50       9 my $modifier = $target_db =~ /SQLServer/ ? "COLUMN " : '';
476 2         12 push @diffs, "ALTER TABLE $t2_name DROP $modifier$f2_name;";
477             }
478             }
479             }
480              
481 5 100       105 if (@new_tables) {
482 1         35 my $dummy_tr = SQL::Translator->new;
483 1         74 $dummy_tr->schema->add_table($_) for @new_tables;
484 1         27 my $producer = $dummy_tr->producer($target_db);
485 1         5 unshift @diffs, $producer->($dummy_tr);
486             }
487 5         22 push(@diffs, @diffs_at_end);
488              
489 5 100       20 if (@diffs) {
490 3 100       27 if ($source_db !~ /^(MySQL|SQLServer|Oracle)$/) {
491 1         5 unshift(@diffs, "-- Target database $target_db is untested/unsupported!!!");
492             }
493             }
494              
495 5 100       19 if (@diffs) {
496 3         44 print join("\n", "-- Convert schema '$s2_name' to '$s1_name':\n", @diffs, "\n");
497 3         1149 exit(1);
498             } else {
499 2         896 print "There were no differences.\n";
500             }
501              
502             sub constraint_to_string {
503 5     5   14 my $c = shift;
504 5 50       23 my $schema = shift or die "No schema given";
505 5 100       140 my @fields = $c->field_names or return '';
506              
507 4 50       101 if ($c->type eq PRIMARY_KEY) {
    100          
    50          
508 0 0       0 if ($target_db =~ /Oracle/) {
509 0 0       0 return (defined $c->name ? 'CONSTRAINT ' . $c->name . ' ' : '') . 'PRIMARY KEY (' . join(', ', @fields) . ')';
510             } else {
511 0         0 return 'PRIMARY KEY (' . join(', ', @fields) . ')';
512             }
513             } elsif ($c->type eq UNIQUE) {
514 2 50       53 if ($target_db =~ /Oracle/) {
515 0 0       0 return (defined $c->name ? 'CONSTRAINT ' . $c->name . ' ' : '') . 'UNIQUE (' . join(', ', @fields) . ')';
516             } else {
517 2 50       53 return 'UNIQUE ' . (defined $c->name ? $c->name . ' ' : '') . '(' . join(', ', @fields) . ')';
518             }
519             } elsif ($c->type eq FOREIGN_KEY) {
520 2 50       105 my $def = join(' ', map { $_ || () } 'CONSTRAINT', $c->name, 'FOREIGN KEY');
  6         29  
521              
522 2         26 $def .= ' (' . join(', ', @fields) . ')';
523              
524 2         14 $def .= ' REFERENCES ' . $c->reference_table;
525              
526 2 50       62 my @rfields = map { $_ || () } $c->reference_fields;
  2         15  
527 2 50       8 unless (@rfields) {
528 0         0 my $rtable_name = $c->reference_table;
529 0 0       0 if (my $ref_table = $schema->get_table($rtable_name)) {
530 0         0 push @rfields, $ref_table->primary_key;
531             } else {
532 0         0 warn "Can't find reference table '$rtable_name' " . "in schema\n";
533             }
534             }
535              
536 2 50       8 if (@rfields) {
537 2         11 $def .= ' (' . join(', ', @rfields) . ')';
538             } else {
539 0         0 warn "FK constraint on " . 'some table' . '.' . join('', @fields) . " has no reference fields\n";
540             }
541              
542 2 50       65 if ($c->match_type) {
543 0 0       0 $def .= ' MATCH ' . ($c->match_type =~ /full/i) ? 'FULL' : 'PARTIAL';
544             }
545              
546 2 100       105 if ($c->on_delete) {
547 1         26 $def .= ' ON DELETE ' . join(' ', $c->on_delete);
548             }
549              
550 2 50       190 if ($c->on_update) {
551 0         0 $def .= ' ON UPDATE ' . join(' ', $c->on_update);
552             }
553              
554 2         17 return $def;
555             }
556             }
557              
558             # -------------------------------------------------------------------
559             # Bring out number weight & measure in a year of dearth.
560             # William Blake
561             # -------------------------------------------------------------------
562              
563             =pod
564              
565             =head1 AUTHOR
566              
567             Ken Youens-Clark Ekclark@cpan.orgE.
568              
569             =head1 SEE ALSO
570              
571             SQL::Translator, L.
572              
573             =cut