Tweaked filter interface to take args as a list.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / MySQL.pm
CommitLineData
16dc9970 1package SQL::Translator::Parser::MySQL;
2
49e1eb70 3# -------------------------------------------------------------------
e78d62f2 4# $Id: MySQL.pm,v 1.46 2005-06-07 16:49:55 kycl4rk Exp $
49e1eb70 5# -------------------------------------------------------------------
90075866 6# Copyright (C) 2002-4 SQLFairy Authors
077ebf34 7#
8# This program is free software; you can redistribute it and/or
9# modify it under the terms of the GNU General Public License as
10# published by the Free Software Foundation; version 2.
11#
12# This program is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15# General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20# 02111-1307 USA
21# -------------------------------------------------------------------
16dc9970 22
d529894e 23=head1 NAME
24
25SQL::Translator::Parser::MySQL - parser for MySQL
26
27=head1 SYNOPSIS
28
29 use SQL::Translator;
30 use SQL::Translator::Parser::MySQL;
31
32 my $translator = SQL::Translator->new;
33 $translator->parser("SQL::Translator::Parser::MySQL");
34
35=head1 DESCRIPTION
36
37The grammar is influenced heavily by Tim Bunce's "mysql2ora" grammar.
38
629b76f9 39Here's the word from the MySQL site
40(http://www.mysql.com/doc/en/CREATE_TABLE.html):
41
42 CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)]
43 [table_options] [select_statement]
44
45 or
46
47 CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name LIKE old_table_name;
48
49 create_definition:
50 col_name type [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT]
51 [PRIMARY KEY] [reference_definition]
52 or PRIMARY KEY (index_col_name,...)
53 or KEY [index_name] (index_col_name,...)
54 or INDEX [index_name] (index_col_name,...)
55 or UNIQUE [INDEX] [index_name] (index_col_name,...)
56 or FULLTEXT [INDEX] [index_name] (index_col_name,...)
57 or [CONSTRAINT symbol] FOREIGN KEY [index_name] (index_col_name,...)
58 [reference_definition]
59 or CHECK (expr)
60
61 type:
62 TINYINT[(length)] [UNSIGNED] [ZEROFILL]
63 or SMALLINT[(length)] [UNSIGNED] [ZEROFILL]
64 or MEDIUMINT[(length)] [UNSIGNED] [ZEROFILL]
65 or INT[(length)] [UNSIGNED] [ZEROFILL]
66 or INTEGER[(length)] [UNSIGNED] [ZEROFILL]
67 or BIGINT[(length)] [UNSIGNED] [ZEROFILL]
68 or REAL[(length,decimals)] [UNSIGNED] [ZEROFILL]
69 or DOUBLE[(length,decimals)] [UNSIGNED] [ZEROFILL]
70 or FLOAT[(length,decimals)] [UNSIGNED] [ZEROFILL]
71 or DECIMAL(length,decimals) [UNSIGNED] [ZEROFILL]
72 or NUMERIC(length,decimals) [UNSIGNED] [ZEROFILL]
73 or CHAR(length) [BINARY]
74 or VARCHAR(length) [BINARY]
75 or DATE
76 or TIME
77 or TIMESTAMP
78 or DATETIME
79 or TINYBLOB
80 or BLOB
81 or MEDIUMBLOB
82 or LONGBLOB
83 or TINYTEXT
84 or TEXT
85 or MEDIUMTEXT
86 or LONGTEXT
87 or ENUM(value1,value2,value3,...)
88 or SET(value1,value2,value3,...)
89
90 index_col_name:
91 col_name [(length)]
92
93 reference_definition:
94 REFERENCES tbl_name [(index_col_name,...)]
95 [MATCH FULL | MATCH PARTIAL]
96 [ON DELETE reference_option]
97 [ON UPDATE reference_option]
98
99 reference_option:
100 RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT
101
102 table_options:
103 TYPE = {BDB | HEAP | ISAM | InnoDB | MERGE | MRG_MYISAM | MYISAM }
104 or AUTO_INCREMENT = #
105 or AVG_ROW_LENGTH = #
106 or CHECKSUM = {0 | 1}
107 or COMMENT = "string"
108 or MAX_ROWS = #
109 or MIN_ROWS = #
110 or PACK_KEYS = {0 | 1 | DEFAULT}
111 or PASSWORD = "string"
112 or DELAY_KEY_WRITE = {0 | 1}
113 or ROW_FORMAT= { default | dynamic | fixed | compressed }
114 or RAID_TYPE= {1 | STRIPED | RAID0 } RAID_CHUNKS=# RAID_CHUNKSIZE=#
115 or UNION = (table_name,[table_name...])
116 or INSERT_METHOD= {NO | FIRST | LAST }
117 or DATA DIRECTORY="absolute path to directory"
118 or INDEX DIRECTORY="absolute path to directory"
119
13aec984 120A subset of the ALTER TABLE syntax that allows addition of foreign keys:
121
122 ALTER [IGNORE] TABLE tbl_name alter_specification [, alter_specification] ...
123
124 alter_specification:
125 ADD [CONSTRAINT [symbol]]
126 FOREIGN KEY [index_name] (index_col_name,...)
127 [reference_definition]
128
129A subset of INSERT that we ignore:
130
131 INSERT anything
132
d529894e 133=cut
134
16dc9970 135use strict;
d529894e 136use vars qw[ $DEBUG $VERSION $GRAMMAR @EXPORT_OK ];
e78d62f2 137$VERSION = sprintf "%d.%02d", q$Revision: 1.46 $ =~ /(\d+)\.(\d+)/;
8d0f3086 138$DEBUG = 0 unless defined $DEBUG;
077ebf34 139
d529894e 140use Data::Dumper;
077ebf34 141use Parse::RecDescent;
142use Exporter;
143use base qw(Exporter);
144
145@EXPORT_OK = qw(parse);
146
d529894e 147# Enable warnings within the Parse::RecDescent module.
148$::RD_ERRORS = 1; # Make sure the parser dies when it encounters an error
149$::RD_WARN = 1; # Enable warnings. This will warn on unused rules &c.
150$::RD_HINT = 1; # Give out hints to help fix problems.
151
d529894e 152$GRAMMAR = q!
153
8ccdeb42 154{
13aec984 155 my ( $database_name, %tables, $table_order, @table_comments );
8ccdeb42 156}
d529894e 157
629b76f9 158#
159# The "eofile" rule makes the parser fail if any "statement" rule
160# fails. Otherwise, the first successful match by a "statement"
161# won't cause the failure needed to know that the parse, as a whole,
162# failed. -ky
163#
13aec984 164startrule : statement(s) eofile {
165 { tables => \%tables, database_name => $database_name }
166}
629b76f9 167
168eofile : /^\Z/
d529894e 169
170statement : comment
dcb4fa06 171 | use
33d0d6d4 172 | set
61745327 173 | drop
d529894e 174 | create
13aec984 175 | alter
176 | insert
d529894e 177 | <error>
178
dcb4fa06 179use : /use/i WORD ';'
13aec984 180 {
181 $database_name = $item[2];
182 @table_comments = ();
183 }
dcb4fa06 184
33d0d6d4 185set : /set/i /[^;]+/ ';'
c5dabd71 186 { @table_comments = () }
734dfc91 187
2e8dfb76 188drop : /drop/i TABLE /[^;]+/ ';'
33d0d6d4 189
61745327 190drop : /drop/i WORD(s) ';'
c5dabd71 191 { @table_comments = () }
61745327 192
13aec984 193insert : /insert/i /[^;]+/ ';'
194
195alter : ALTER TABLE table_name alter_specification(s /,/) ';'
196 {
197 my $table_name = $item{'table_name'};
198 die "Cannot ALTER table '$table_name'; it does not exist"
199 unless $tables{ $table_name };
200 for my $definition ( @{ $item[4] } ) {
201 $definition->{'extra'}->{'alter'} = 1;
202 push @{ $tables{ $table_name }{'constraints'} }, $definition;
203 }
204 }
205
206alter_specification : ADD foreign_key_def
207 { $return = $item[2] }
208
dcb4fa06 209create : CREATE /database/i WORD ';'
c5dabd71 210 { @table_comments = () }
dcb4fa06 211
13aec984 212create : CREATE TEMPORARY(?) TABLE opt_if_not_exists(?) table_name '(' create_definition(s /,/) /(,\s*)?\)/ table_option(s?) ';'
d529894e 213 {
214 my $table_name = $item{'table_name'};
215 $tables{ $table_name }{'order'} = ++$table_order;
216 $tables{ $table_name }{'table_name'} = $table_name;
217
734dfc91 218 if ( @table_comments ) {
219 $tables{ $table_name }{'comments'} = [ @table_comments ];
220 @table_comments = ();
221 }
222
61745327 223 my $i = 1;
40c1ade1 224 for my $definition ( @{ $item[7] } ) {
f2cf1734 225 if ( $definition->{'supertype'} eq 'field' ) {
d529894e 226 my $field_name = $definition->{'name'};
227 $tables{ $table_name }{'fields'}{ $field_name } =
228 { %$definition, order => $i };
229 $i++;
230
231 if ( $definition->{'is_primary_key'} ) {
f2cf1734 232 push @{ $tables{ $table_name }{'constraints'} },
d529894e 233 {
234 type => 'primary_key',
235 fields => [ $field_name ],
16dc9970 236 }
d529894e 237 ;
238 }
dd2ef5ae 239 }
f2cf1734 240 elsif ( $definition->{'supertype'} eq 'constraint' ) {
f2cf1734 241 push @{ $tables{ $table_name }{'constraints'} }, $definition;
40c1ade1 242 }
f2cf1734 243 elsif ( $definition->{'supertype'} eq 'index' ) {
734dfc91 244 push @{ $tables{ $table_name }{'indices'} }, $definition;
dd2ef5ae 245 }
d529894e 246 }
dd2ef5ae 247
02a21f1a 248 if ( my @options = @{ $item{'table_option(s?)'} } ) {
249 $tables{ $table_name }{'table_options'} = \@options;
d529894e 250 }
58a88238 251
252 1;
d529894e 253 }
dd2ef5ae 254
40c1ade1 255opt_if_not_exists : /if not exists/i
256
f2cf1734 257create : CREATE UNIQUE(?) /(index|key)/i index_name /on/i table_name '(' field_name(s /,/) ')' ';'
d529894e 258 {
734dfc91 259 @table_comments = ();
d529894e 260 push @{ $tables{ $item{'table_name'} }{'indices'} },
261 {
262 name => $item[4],
263 type => $item[2] ? 'unique' : 'normal',
264 fields => $item[8],
dd2ef5ae 265 }
d529894e 266 ;
267 }
dd2ef5ae 268
f2cf1734 269create_definition : constraint
270 | index
d529894e 271 | field
02a21f1a 272 | comment
d529894e 273 | <error>
274
734dfc91 275comment : /^\s*(?:#|-{2}).*\n/
276 {
277 my $comment = $item[1];
a82fa2cb 278 $comment =~ s/^\s*(#|--)\s*//;
734dfc91 279 $comment =~ s/\s*$//;
280 $return = $comment;
281 push @table_comments, $comment;
282 }
283
e78d62f2 284comment : /\/\*/ /[^\*]+/ /\*\// ';'
285 {
286 my $comment = $item[2];
287 $comment =~ s/^\s*|\s*$//g;
288 $return = $comment;
289 }
290
734dfc91 291field_comment : /^\s*(?:#|-{2}).*\n/
292 {
293 my $comment = $item[1];
a82fa2cb 294 $comment =~ s/^\s*(#|--)\s*//;
734dfc91 295 $comment =~ s/\s*$//;
296 $return = $comment;
297 }
d529894e 298
299blank : /\s*/
300
734dfc91 301field : field_comment(s?) field_name data_type field_qualifier(s?) reference_definition(?) field_comment(s?)
d529894e 302 {
734dfc91 303 my %qualifiers = map { %$_ } @{ $item{'field_qualifier(s?)'} || [] };
d529894e 304 if ( my @type_quals = @{ $item{'data_type'}{'qualifiers'} || [] } ) {
305 $qualifiers{ $_ } = 1 for @type_quals;
306 }
307
c5dabd71 308 my $null = defined $qualifiers{'not_null'}
309 ? $qualifiers{'not_null'} : 1;
310 delete $qualifiers{'not_null'};
311
88b89793 312 my @comments = ( @{ $item[1] }, @{ $item[6] } );
313
d529894e 314 $return = {
f2cf1734 315 supertype => 'field',
316 name => $item{'field_name'},
317 data_type => $item{'data_type'}{'type'},
318 size => $item{'data_type'}{'size'},
319 list => $item{'data_type'}{'list'},
320 null => $null,
321 constraints => $item{'reference_definition(?)'},
88b89793 322 comments => [ @comments ],
d529894e 323 %qualifiers,
324 }
325 }
326 | <error>
dd2ef5ae 327
d529894e 328field_qualifier : not_null
329 {
330 $return = {
331 null => $item{'not_null'},
332 }
333 }
16dc9970 334
d529894e 335field_qualifier : default_val
336 {
337 $return = {
338 default => $item{'default_val'},
339 }
340 }
16dc9970 341
d529894e 342field_qualifier : auto_inc
343 {
344 $return = {
345 is_auto_inc => $item{'auto_inc'},
346 }
347 }
16dc9970 348
d529894e 349field_qualifier : primary_key
350 {
351 $return = {
352 is_primary_key => $item{'primary_key'},
353 }
354 }
16dc9970 355
d529894e 356field_qualifier : unsigned
357 {
358 $return = {
359 is_unsigned => $item{'unsigned'},
360 }
361 }
16dc9970 362
095b4549 363field_qualifier : /character set/i WORD
364 {
365 $return = {
366 character_set => $item[2],
367 }
368 }
369
658637cd 370reference_definition : /references/i table_name parens_field_list(?) match_type(?) on_delete_do(?) on_update_do(?)
371 {
40c1ade1 372 $return = {
658637cd 373 type => 'foreign_key',
374 reference_table => $item[2],
375 reference_fields => $item[3][0],
376 match_type => $item[4][0],
377 on_delete_do => $item[5][0],
378 on_update_do => $item[6][0],
379 }
380 }
381
02a21f1a 382match_type : /match full/i { 'full' }
658637cd 383 |
02a21f1a 384 /match partial/i { 'partial' }
658637cd 385
386on_delete_do : /on delete/i reference_option
387 { $item[2] }
388
389on_update_do : /on update/i reference_option
390 { $item[2] }
391
392reference_option: /restrict/i |
393 /cascade/i |
394 /set null/i |
395 /no action/i |
396 /set default/i
397 { $item[1] }
398
f2cf1734 399index : normal_index
371f5f88 400 | fulltext_index
58a88238 401 | <error>
d529894e 402
0d41bc9b 403table_name : NAME
d529894e 404
0d41bc9b 405field_name : NAME
d529894e 406
02a21f1a 407index_name : NAME
d529894e 408
409data_type : WORD parens_value_list(s?) type_qualifier(s?)
410 {
411 my $type = $item[1];
412 my $size; # field size, applicable only to non-set fields
413 my $list; # set list, applicable only to sets (duh)
414
44fcd0b5 415 if ( uc($type) =~ /^(SET|ENUM)$/ ) {
d529894e 416 $size = undef;
417 $list = $item[2][0];
418 }
419 else {
420 $size = $item[2][0];
421 $list = [];
422 }
423
6333c482 424 unless ( @{ $size || [] } ) {
425 if ( lc $type eq 'tinyint' ) {
7898fb70 426 $size = 4;
6333c482 427 }
428 elsif ( lc $type eq 'smallint' ) {
7898fb70 429 $size = 6;
6333c482 430 }
431 elsif ( lc $type eq 'mediumint' ) {
7898fb70 432 $size = 9;
6333c482 433 }
e78d62f2 434 elsif ( $type =~ /^int(eger)?$/i ) {
f2cf1734 435 $type = 'int';
7898fb70 436 $size = 11;
6333c482 437 }
438 elsif ( lc $type eq 'bigint' ) {
7898fb70 439 $size = 20;
6333c482 440 }
1eb08c80 441 elsif (
442 lc $type =~ /(float|double|decimal|numeric|real|fixed|dec)/
443 ) {
6333c482 444 $size = [8,2];
445 }
446 }
447
7898fb70 448 if ( $type =~ /^tiny(text|blob)$/i ) {
449 $size = 255;
256d534a 450 }
7898fb70 451 elsif ( $type =~ /^(blob|text)$/i ) {
452 $size = 65_535;
256d534a 453 }
7898fb70 454 elsif ( $type =~ /^medium(blob|text)$/i ) {
455 $size = 16_777_215;
256d534a 456 }
7898fb70 457 elsif ( $type =~ /^long(blob|text)$/i ) {
458 $size = 4_294_967_295;
256d534a 459 }
460
d529894e 461 $return = {
462 type => $type,
463 size => $size,
464 list => $list,
465 qualifiers => $item[3],
466 }
467 }
16dc9970 468
658637cd 469parens_field_list : '(' field_name(s /,/) ')'
470 { $item[2] }
471
d529894e 472parens_value_list : '(' VALUE(s /,/) ')'
473 { $item[2] }
16dc9970 474
d529894e 475type_qualifier : /(BINARY|UNSIGNED|ZEROFILL)/i
476 { lc $item[1] }
16dc9970 477
d529894e 478field_type : WORD
16dc9970 479
d529894e 480create_index : /create/i /index/i
dd2ef5ae 481
e78d62f2 482not_null : /not/i /null/i
483 { $return = 0 }
484 |
485 /null/i
486 { $return = 1 }
16dc9970 487
d529894e 488unsigned : /unsigned/i { $return = 0 }
16dc9970 489
09fa21a6 490#default_val : /default/i /(?:')?[\s\w\d:.-]*(?:')?/
491# {
492# $item[2] =~ s/'//g;
493# $return = $item[2];
494# }
495
496default_val : /default/i /'(?:.*?\\')*.*?'|(?:')?[\w\d:.-]*(?:')?/
497 {
498 $item[2] =~ s/^\s*'|'\s*$//g;
d529894e 499 $return = $item[2];
500 }
16dc9970 501
d529894e 502auto_inc : /auto_increment/i { 1 }
16dc9970 503
d529894e 504primary_key : /primary/i /key/i { 1 }
16dc9970 505
f2cf1734 506constraint : primary_key_def
507 | unique_key_def
508 | foreign_key_def
509 | <error>
510
02a21f1a 511foreign_key_def : foreign_key_def_begin parens_field_list reference_definition
40c1ade1 512 {
513 $return = {
f2cf1734 514 supertype => 'constraint',
40c1ade1 515 type => 'foreign_key',
02a21f1a 516 name => $item[1],
09fa21a6 517 fields => $item[2],
40c1ade1 518 %{ $item{'reference_definition'} },
519 }
520 }
521
e78d62f2 522foreign_key_def_begin : /constraint/i /foreign key/i WORD
523 { $return = $item[3] }
524 |
525 /constraint/i NAME /foreign key/i
526 { $return = $item[2] }
527 |
528 /constraint/i /foreign key/i
02a21f1a 529 { $return = '' }
530 |
e78d62f2 531 /foreign key/i WORD
02a21f1a 532 { $return = $item[2] }
533 |
534 /foreign key/i
535 { $return = '' }
40c1ade1 536
1853ba82 537primary_key_def : primary_key index_name(?) '(' name_with_opt_paren(s /,/) ')'
d529894e 538 {
f2cf1734 539 $return = {
540 supertype => 'constraint',
541 name => $item{'index_name(?)'}[0],
542 type => 'primary_key',
543 fields => $item[4],
58a88238 544 };
d529894e 545 }
16dc9970 546
f2cf1734 547unique_key_def : UNIQUE KEY(?) index_name(?) '(' name_with_opt_paren(s /,/) ')'
d529894e 548 {
f2cf1734 549 $return = {
550 supertype => 'constraint',
551 name => $item{'index_name(?)'}[0],
552 type => 'unique',
553 fields => $item[5],
d529894e 554 }
555 }
16dc9970 556
f2cf1734 557normal_index : KEY index_name(?) '(' name_with_opt_paren(s /,/) ')'
d529894e 558 {
f2cf1734 559 $return = {
560 supertype => 'index',
561 type => 'normal',
562 name => $item{'index_name(?)'}[0],
563 fields => $item[4],
d529894e 564 }
565 }
16dc9970 566
f2cf1734 567fulltext_index : /fulltext/i KEY(?) index_name(?) '(' name_with_opt_paren(s /,/) ')'
371f5f88 568 {
f2cf1734 569 $return = {
570 supertype => 'index',
571 type => 'fulltext',
572 name => $item{'index_name(?)'}[0],
573 fields => $item[5],
371f5f88 574 }
575 }
576
d529894e 577name_with_opt_paren : NAME parens_value_list(s?)
578 { $item[2][0] ? "$item[1]($item[2][0][0])" : $item[1] }
16dc9970 579
f2cf1734 580UNIQUE : /unique/i { 1 }
16dc9970 581
f2cf1734 582KEY : /key/i | /index/i
16dc9970 583
e78d62f2 584table_option : 'DEFAULT CHARSET' /\s*=\s*/ WORD
585 {
586 $return = { $item[1] => $item[3] };
587 }
588 | WORD /\s*=\s*/ WORD
d529894e 589 {
02a21f1a 590 $return = { $item[1] => $item[3] };
d529894e 591 }
16dc9970 592
13aec984 593ADD : /add/i
594
595ALTER : /alter/i
596
40c1ade1 597CREATE : /create/i
598
599TEMPORARY : /temporary/i
600
601TABLE : /table/i
602
d529894e 603WORD : /\w+/
16dc9970 604
d529894e 605DIGITS : /\d+/
16dc9970 606
d529894e 607COMMA : ','
16dc9970 608
d529894e 609NAME : "`" /\w+/ "`"
610 { $item[2] }
611 | /\w+/
612 { $item[1] }
16dc9970 613
d529894e 614VALUE : /[-+]?\.?\d+(?:[eE]\d+)?/
615 { $item[1] }
f2cf1734 616 | /'.*?'/
617 {
618 # remove leading/trailing quotes
619 my $val = $item[1];
620 $val =~ s/^['"]|['"]$//g;
621 $return = $val;
622 }
d529894e 623 | /NULL/
624 { 'NULL' }
16dc9970 625
d529894e 626!;
16dc9970 627
d529894e 628# -------------------------------------------------------------------
629sub parse {
70944bc5 630 my ( $translator, $data ) = @_;
40c1ade1 631 my $parser = Parse::RecDescent->new($GRAMMAR);
077ebf34 632
e099bee9 633 local $::RD_TRACE = $translator->trace ? 1 : undef;
634 local $DEBUG = $translator->debug;
d529894e 635
636 unless (defined $parser) {
637 return $translator->error("Error instantiating Parse::RecDescent ".
638 "instance: Bad grammer");
639 }
640
641 my $result = $parser->startrule($data);
40c1ade1 642 return $translator->error( "Parse failed." ) unless defined $result;
13aec984 643 warn "Parse result:".Dumper( $result ) if $DEBUG;
8ccdeb42 644
70944bc5 645 my $schema = $translator->schema;
13aec984 646 $schema->name($result->{'database_name'}) if $result->{'database_name'};
647
034ecdec 648 my @tables = sort {
13aec984 649 $result->{'tables'}{ $a }{'order'}
650 <=>
651 $result->{'tables'}{ $b }{'order'}
652 } keys %{ $result->{'tables'} };
034ecdec 653
654 for my $table_name ( @tables ) {
13aec984 655 my $tdata = $result->{tables}{ $table_name };
8ccdeb42 656 my $table = $schema->add_table(
657 name => $tdata->{'table_name'},
40c1ade1 658 ) or die $schema->error;
8ccdeb42 659
734dfc91 660 $table->comments( $tdata->{'comments'} );
f2cf1734 661
8ccdeb42 662 my @fields = sort {
663 $tdata->{'fields'}->{$a}->{'order'}
664 <=>
665 $tdata->{'fields'}->{$b}->{'order'}
666 } keys %{ $tdata->{'fields'} };
667
668 for my $fname ( @fields ) {
669 my $fdata = $tdata->{'fields'}{ $fname };
670 my $field = $table->add_field(
671 name => $fdata->{'name'},
672 data_type => $fdata->{'data_type'},
673 size => $fdata->{'size'},
674 default_value => $fdata->{'default'},
675 is_auto_increment => $fdata->{'is_auto_inc'},
676 is_nullable => $fdata->{'null'},
88b89793 677 comments => $fdata->{'comments'},
40c1ade1 678 ) or die $table->error;
f2cf1734 679
680 $table->primary_key( $field->name ) if $fdata->{'is_primary_key'};
681
682 for my $qual ( qw[ binary unsigned zerofill list ] ) {
683 if ( my $val = $fdata->{ $qual } || $fdata->{ uc $qual } ) {
684 next if ref $val eq 'ARRAY' && !@$val;
685 $field->extra( $qual, $val );
686 }
687 }
688
689 if ( $field->data_type =~ /(set|enum)/i && !$field->size ) {
690 my %extra = $field->extra;
d14fe688 691 my $longest = 0;
f2cf1734 692 for my $len ( map { length } @{ $extra{'list'} || [] } ) {
693 $longest = $len if $len > $longest;
694 }
695 $field->size( $longest ) if $longest;
696 }
697
698 for my $cdata ( @{ $fdata->{'constraints'} } ) {
699 next unless $cdata->{'type'} eq 'foreign_key';
700 $cdata->{'fields'} ||= [ $field->name ];
701 push @{ $tdata->{'constraints'} }, $cdata;
702 }
703 }
704
705 for my $idata ( @{ $tdata->{'indices'} || [] } ) {
706 my $index = $table->add_index(
707 name => $idata->{'name'},
708 type => uc $idata->{'type'},
709 fields => $idata->{'fields'},
710 ) or die $table->error;
711 }
712
02a21f1a 713 if ( my @options = @{ $tdata->{'table_options'} || [] } ) {
714 $table->options( \@options ) or die $table->error;
715 }
716
f2cf1734 717 for my $cdata ( @{ $tdata->{'constraints'} || [] } ) {
718 my $constraint = $table->add_constraint(
719 name => $cdata->{'name'},
720 type => $cdata->{'type'},
721 fields => $cdata->{'fields'},
722 reference_table => $cdata->{'reference_table'},
723 reference_fields => $cdata->{'reference_fields'},
724 match_type => $cdata->{'match_type'} || '',
725 on_delete => $cdata->{'on_delete_do'},
726 on_update => $cdata->{'on_update_do'},
727 ) or die $table->error;
8ccdeb42 728 }
729 }
730
f62bd16c 731 return 1;
d529894e 732}
733
7341;
735
034ecdec 736# -------------------------------------------------------------------
d529894e 737# Where man is not nature is barren.
738# William Blake
034ecdec 739# -------------------------------------------------------------------
16dc9970 740
d529894e 741=pod
16dc9970 742
743=head1 AUTHOR
744
d529894e 745Ken Y. Clark E<lt>kclark@cpan.orgE<gt>,
8ccdeb42 746Chris Mungall E<lt>cjm@fruitfly.orgE<gt>.
16dc9970 747
748=head1 SEE ALSO
749
8ccdeb42 750perl(1), Parse::RecDescent, SQL::Translator::Schema.
16dc9970 751
752=cut