A whole lot of changes, but major additions include adding diffs for table options...
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / MySQL.pm
CommitLineData
16dc9970 1package SQL::Translator::Parser::MySQL;
2
49e1eb70 3# -------------------------------------------------------------------
bd30a9a2 4# $Id: MySQL.pm,v 1.49 2005-06-27 20:36:42 duality72 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 ];
bd30a9a2 137$VERSION = sprintf "%d.%02d", q$Revision: 1.49 $ =~ /(\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?)'} } ) {
35843e6b 249 for my $option ( @options ) {
250 my ( $key, $value ) = each %$option;
251 if ( $key eq 'comment' ) {
252 push @{ $tables{ $table_name }{'comments'} }, $value;
253 }
254 else {
255 push @{ $tables{ $table_name }{'table_options'} }, $option;
256 }
257 }
d529894e 258 }
58a88238 259
260 1;
d529894e 261 }
dd2ef5ae 262
40c1ade1 263opt_if_not_exists : /if not exists/i
264
f2cf1734 265create : CREATE UNIQUE(?) /(index|key)/i index_name /on/i table_name '(' field_name(s /,/) ')' ';'
d529894e 266 {
734dfc91 267 @table_comments = ();
d529894e 268 push @{ $tables{ $item{'table_name'} }{'indices'} },
269 {
270 name => $item[4],
271 type => $item[2] ? 'unique' : 'normal',
272 fields => $item[8],
dd2ef5ae 273 }
d529894e 274 ;
275 }
dd2ef5ae 276
f2cf1734 277create_definition : constraint
278 | index
d529894e 279 | field
02a21f1a 280 | comment
d529894e 281 | <error>
282
734dfc91 283comment : /^\s*(?:#|-{2}).*\n/
284 {
285 my $comment = $item[1];
a82fa2cb 286 $comment =~ s/^\s*(#|--)\s*//;
734dfc91 287 $comment =~ s/\s*$//;
288 $return = $comment;
289 push @table_comments, $comment;
290 }
291
e78d62f2 292comment : /\/\*/ /[^\*]+/ /\*\// ';'
293 {
294 my $comment = $item[2];
295 $comment =~ s/^\s*|\s*$//g;
296 $return = $comment;
297 }
298
734dfc91 299field_comment : /^\s*(?:#|-{2}).*\n/
300 {
301 my $comment = $item[1];
a82fa2cb 302 $comment =~ s/^\s*(#|--)\s*//;
734dfc91 303 $comment =~ s/\s*$//;
304 $return = $comment;
305 }
d529894e 306
35843e6b 307
308field_comment2 : /comment/i /'.*?'/
309 {
310 my $comment = $item[2];
311 $comment =~ s/^'//;
312 $comment =~ s/'$//;
313 $return = $comment;
314 }
315
d529894e 316blank : /\s*/
317
35843e6b 318field : field_comment(s?) field_name data_type field_qualifier(s?) field_comment2(?) reference_definition(?) on_update_do(?) field_comment(s?)
d529894e 319 {
734dfc91 320 my %qualifiers = map { %$_ } @{ $item{'field_qualifier(s?)'} || [] };
d529894e 321 if ( my @type_quals = @{ $item{'data_type'}{'qualifiers'} || [] } ) {
322 $qualifiers{ $_ } = 1 for @type_quals;
323 }
324
c5dabd71 325 my $null = defined $qualifiers{'not_null'}
326 ? $qualifiers{'not_null'} : 1;
327 delete $qualifiers{'not_null'};
328
35843e6b 329 my @comments = ( @{ $item[1] }, @{ $item[5] }, @{ $item[8] } );
88b89793 330
d529894e 331 $return = {
f2cf1734 332 supertype => 'field',
333 name => $item{'field_name'},
334 data_type => $item{'data_type'}{'type'},
335 size => $item{'data_type'}{'size'},
336 list => $item{'data_type'}{'list'},
337 null => $null,
338 constraints => $item{'reference_definition(?)'},
88b89793 339 comments => [ @comments ],
d529894e 340 %qualifiers,
341 }
342 }
343 | <error>
dd2ef5ae 344
d529894e 345field_qualifier : not_null
346 {
347 $return = {
348 null => $item{'not_null'},
349 }
350 }
16dc9970 351
d529894e 352field_qualifier : default_val
353 {
354 $return = {
355 default => $item{'default_val'},
356 }
357 }
16dc9970 358
d529894e 359field_qualifier : auto_inc
360 {
361 $return = {
362 is_auto_inc => $item{'auto_inc'},
363 }
364 }
16dc9970 365
d529894e 366field_qualifier : primary_key
367 {
368 $return = {
369 is_primary_key => $item{'primary_key'},
370 }
371 }
16dc9970 372
d529894e 373field_qualifier : unsigned
374 {
375 $return = {
376 is_unsigned => $item{'unsigned'},
377 }
378 }
16dc9970 379
095b4549 380field_qualifier : /character set/i WORD
381 {
382 $return = {
bd30a9a2 383 'CHARACTER SET' => $item[2],
384 }
385 }
386
387field_qualifier : /collate/i WORD
388 {
389 $return = {
390 COLLATE => $item[2],
391 }
392 }
393
394field_qualifier : /on update/i CURRENT_TIMESTAMP
395 {
396 $return = {
397 'ON UPDATE' => $item[2],
095b4549 398 }
399 }
400
658637cd 401reference_definition : /references/i table_name parens_field_list(?) match_type(?) on_delete_do(?) on_update_do(?)
402 {
40c1ade1 403 $return = {
658637cd 404 type => 'foreign_key',
405 reference_table => $item[2],
406 reference_fields => $item[3][0],
407 match_type => $item[4][0],
408 on_delete_do => $item[5][0],
409 on_update_do => $item[6][0],
410 }
411 }
412
02a21f1a 413match_type : /match full/i { 'full' }
658637cd 414 |
02a21f1a 415 /match partial/i { 'partial' }
658637cd 416
417on_delete_do : /on delete/i reference_option
418 { $item[2] }
419
6fa97af6 420on_update_do :
421 /on update/i 'CURRENT_TIMESTAMP'
422 { $item[2] }
423 |
424 /on update/i reference_option
658637cd 425 { $item[2] }
426
427reference_option: /restrict/i |
428 /cascade/i |
429 /set null/i |
430 /no action/i |
431 /set default/i
432 { $item[1] }
433
f2cf1734 434index : normal_index
371f5f88 435 | fulltext_index
58a88238 436 | <error>
d529894e 437
0d41bc9b 438table_name : NAME
d529894e 439
0d41bc9b 440field_name : NAME
d529894e 441
02a21f1a 442index_name : NAME
d529894e 443
444data_type : WORD parens_value_list(s?) type_qualifier(s?)
445 {
446 my $type = $item[1];
447 my $size; # field size, applicable only to non-set fields
448 my $list; # set list, applicable only to sets (duh)
449
44fcd0b5 450 if ( uc($type) =~ /^(SET|ENUM)$/ ) {
d529894e 451 $size = undef;
452 $list = $item[2][0];
453 }
454 else {
455 $size = $item[2][0];
456 $list = [];
457 }
458
6333c482 459 unless ( @{ $size || [] } ) {
460 if ( lc $type eq 'tinyint' ) {
7898fb70 461 $size = 4;
6333c482 462 }
463 elsif ( lc $type eq 'smallint' ) {
7898fb70 464 $size = 6;
6333c482 465 }
466 elsif ( lc $type eq 'mediumint' ) {
7898fb70 467 $size = 9;
6333c482 468 }
e78d62f2 469 elsif ( $type =~ /^int(eger)?$/i ) {
f2cf1734 470 $type = 'int';
7898fb70 471 $size = 11;
6333c482 472 }
473 elsif ( lc $type eq 'bigint' ) {
7898fb70 474 $size = 20;
6333c482 475 }
1eb08c80 476 elsif (
477 lc $type =~ /(float|double|decimal|numeric|real|fixed|dec)/
478 ) {
6333c482 479 $size = [8,2];
480 }
481 }
482
7898fb70 483 if ( $type =~ /^tiny(text|blob)$/i ) {
484 $size = 255;
256d534a 485 }
7898fb70 486 elsif ( $type =~ /^(blob|text)$/i ) {
487 $size = 65_535;
256d534a 488 }
7898fb70 489 elsif ( $type =~ /^medium(blob|text)$/i ) {
490 $size = 16_777_215;
256d534a 491 }
7898fb70 492 elsif ( $type =~ /^long(blob|text)$/i ) {
493 $size = 4_294_967_295;
256d534a 494 }
495
d529894e 496 $return = {
497 type => $type,
498 size => $size,
499 list => $list,
500 qualifiers => $item[3],
501 }
502 }
16dc9970 503
658637cd 504parens_field_list : '(' field_name(s /,/) ')'
505 { $item[2] }
506
d529894e 507parens_value_list : '(' VALUE(s /,/) ')'
508 { $item[2] }
16dc9970 509
d529894e 510type_qualifier : /(BINARY|UNSIGNED|ZEROFILL)/i
511 { lc $item[1] }
16dc9970 512
d529894e 513field_type : WORD
16dc9970 514
d529894e 515create_index : /create/i /index/i
dd2ef5ae 516
e78d62f2 517not_null : /not/i /null/i
518 { $return = 0 }
519 |
520 /null/i
521 { $return = 1 }
16dc9970 522
d529894e 523unsigned : /unsigned/i { $return = 0 }
16dc9970 524
09fa21a6 525#default_val : /default/i /(?:')?[\s\w\d:.-]*(?:')?/
526# {
527# $item[2] =~ s/'//g;
528# $return = $item[2];
529# }
530
6fa97af6 531default_val :
532 /default/i 'CURRENT_TIMESTAMP'
533 {
534 $return = $item[2];
535 }
536 |
537 /default/i /'(?:.*?\\')*.*?'|(?:')?[\w\d:.-]*(?:')?/
09fa21a6 538 {
539 $item[2] =~ s/^\s*'|'\s*$//g;
d529894e 540 $return = $item[2];
541 }
16dc9970 542
d529894e 543auto_inc : /auto_increment/i { 1 }
16dc9970 544
d529894e 545primary_key : /primary/i /key/i { 1 }
16dc9970 546
f2cf1734 547constraint : primary_key_def
548 | unique_key_def
549 | foreign_key_def
550 | <error>
551
02a21f1a 552foreign_key_def : foreign_key_def_begin parens_field_list reference_definition
40c1ade1 553 {
554 $return = {
f2cf1734 555 supertype => 'constraint',
40c1ade1 556 type => 'foreign_key',
02a21f1a 557 name => $item[1],
09fa21a6 558 fields => $item[2],
40c1ade1 559 %{ $item{'reference_definition'} },
560 }
561 }
562
e78d62f2 563foreign_key_def_begin : /constraint/i /foreign key/i WORD
564 { $return = $item[3] }
565 |
566 /constraint/i NAME /foreign key/i
567 { $return = $item[2] }
568 |
569 /constraint/i /foreign key/i
02a21f1a 570 { $return = '' }
571 |
e78d62f2 572 /foreign key/i WORD
02a21f1a 573 { $return = $item[2] }
574 |
575 /foreign key/i
576 { $return = '' }
40c1ade1 577
1853ba82 578primary_key_def : primary_key index_name(?) '(' name_with_opt_paren(s /,/) ')'
d529894e 579 {
f2cf1734 580 $return = {
581 supertype => 'constraint',
582 name => $item{'index_name(?)'}[0],
583 type => 'primary_key',
584 fields => $item[4],
58a88238 585 };
d529894e 586 }
16dc9970 587
f2cf1734 588unique_key_def : UNIQUE KEY(?) index_name(?) '(' name_with_opt_paren(s /,/) ')'
d529894e 589 {
f2cf1734 590 $return = {
591 supertype => 'constraint',
592 name => $item{'index_name(?)'}[0],
593 type => 'unique',
594 fields => $item[5],
d529894e 595 }
596 }
16dc9970 597
f2cf1734 598normal_index : KEY index_name(?) '(' name_with_opt_paren(s /,/) ')'
d529894e 599 {
f2cf1734 600 $return = {
601 supertype => 'index',
602 type => 'normal',
603 name => $item{'index_name(?)'}[0],
604 fields => $item[4],
d529894e 605 }
606 }
16dc9970 607
f2cf1734 608fulltext_index : /fulltext/i KEY(?) index_name(?) '(' name_with_opt_paren(s /,/) ')'
371f5f88 609 {
f2cf1734 610 $return = {
611 supertype => 'index',
612 type => 'fulltext',
613 name => $item{'index_name(?)'}[0],
614 fields => $item[5],
371f5f88 615 }
616 }
617
d529894e 618name_with_opt_paren : NAME parens_value_list(s?)
619 { $item[2][0] ? "$item[1]($item[2][0][0])" : $item[1] }
16dc9970 620
f2cf1734 621UNIQUE : /unique/i { 1 }
16dc9970 622
f2cf1734 623KEY : /key/i | /index/i
16dc9970 624
bd30a9a2 625table_option : WORD /\s*=\s*/ WORD
e78d62f2 626 {
627 $return = { $item[1] => $item[3] };
628 }
35843e6b 629 | /comment/i /=/ /'.*?'/
630 {
631 my $comment = $item[3];
632 $comment =~ s/^'//;
633 $comment =~ s/'$//;
634 $return = { comment => $comment };
635 }
bd30a9a2 636 | /(default )?(charset|character set)/i /\s*=\s*/ WORD
d529894e 637 {
bd30a9a2 638 $return = { 'CHARACTER SET' => $item[3] };
d529894e 639 }
bd30a9a2 640
641default : /default/i
16dc9970 642
13aec984 643ADD : /add/i
644
645ALTER : /alter/i
646
40c1ade1 647CREATE : /create/i
648
649TEMPORARY : /temporary/i
650
651TABLE : /table/i
652
d529894e 653WORD : /\w+/
16dc9970 654
d529894e 655DIGITS : /\d+/
16dc9970 656
d529894e 657COMMA : ','
16dc9970 658
d529894e 659NAME : "`" /\w+/ "`"
660 { $item[2] }
661 | /\w+/
662 { $item[1] }
16dc9970 663
d529894e 664VALUE : /[-+]?\.?\d+(?:[eE]\d+)?/
665 { $item[1] }
f2cf1734 666 | /'.*?'/
667 {
668 # remove leading/trailing quotes
669 my $val = $item[1];
670 $val =~ s/^['"]|['"]$//g;
671 $return = $val;
672 }
d529894e 673 | /NULL/
674 { 'NULL' }
16dc9970 675
bd30a9a2 676CURRENT_TIMESTAMP : /current_timestamp(\(\))?/i
677 | /now\(\)/i
678 { 'CURRENT_TIMESTAMP' }
679
d529894e 680!;
16dc9970 681
d529894e 682# -------------------------------------------------------------------
683sub parse {
70944bc5 684 my ( $translator, $data ) = @_;
40c1ade1 685 my $parser = Parse::RecDescent->new($GRAMMAR);
077ebf34 686
e099bee9 687 local $::RD_TRACE = $translator->trace ? 1 : undef;
688 local $DEBUG = $translator->debug;
d529894e 689
690 unless (defined $parser) {
691 return $translator->error("Error instantiating Parse::RecDescent ".
692 "instance: Bad grammer");
693 }
694
695 my $result = $parser->startrule($data);
40c1ade1 696 return $translator->error( "Parse failed." ) unless defined $result;
13aec984 697 warn "Parse result:".Dumper( $result ) if $DEBUG;
8ccdeb42 698
70944bc5 699 my $schema = $translator->schema;
13aec984 700 $schema->name($result->{'database_name'}) if $result->{'database_name'};
701
034ecdec 702 my @tables = sort {
13aec984 703 $result->{'tables'}{ $a }{'order'}
704 <=>
705 $result->{'tables'}{ $b }{'order'}
706 } keys %{ $result->{'tables'} };
034ecdec 707
708 for my $table_name ( @tables ) {
13aec984 709 my $tdata = $result->{tables}{ $table_name };
8ccdeb42 710 my $table = $schema->add_table(
711 name => $tdata->{'table_name'},
40c1ade1 712 ) or die $schema->error;
8ccdeb42 713
734dfc91 714 $table->comments( $tdata->{'comments'} );
f2cf1734 715
8ccdeb42 716 my @fields = sort {
717 $tdata->{'fields'}->{$a}->{'order'}
718 <=>
719 $tdata->{'fields'}->{$b}->{'order'}
720 } keys %{ $tdata->{'fields'} };
721
722 for my $fname ( @fields ) {
723 my $fdata = $tdata->{'fields'}{ $fname };
724 my $field = $table->add_field(
725 name => $fdata->{'name'},
726 data_type => $fdata->{'data_type'},
727 size => $fdata->{'size'},
728 default_value => $fdata->{'default'},
729 is_auto_increment => $fdata->{'is_auto_inc'},
730 is_nullable => $fdata->{'null'},
88b89793 731 comments => $fdata->{'comments'},
40c1ade1 732 ) or die $table->error;
f2cf1734 733
734 $table->primary_key( $field->name ) if $fdata->{'is_primary_key'};
735
bd30a9a2 736 for my $qual ( qw[ binary unsigned zerofill list collate ],
737 'character set', 'on update' ) {
f2cf1734 738 if ( my $val = $fdata->{ $qual } || $fdata->{ uc $qual } ) {
739 next if ref $val eq 'ARRAY' && !@$val;
740 $field->extra( $qual, $val );
741 }
742 }
743
744 if ( $field->data_type =~ /(set|enum)/i && !$field->size ) {
745 my %extra = $field->extra;
d14fe688 746 my $longest = 0;
f2cf1734 747 for my $len ( map { length } @{ $extra{'list'} || [] } ) {
748 $longest = $len if $len > $longest;
749 }
750 $field->size( $longest ) if $longest;
751 }
752
753 for my $cdata ( @{ $fdata->{'constraints'} } ) {
754 next unless $cdata->{'type'} eq 'foreign_key';
755 $cdata->{'fields'} ||= [ $field->name ];
756 push @{ $tdata->{'constraints'} }, $cdata;
757 }
758 }
759
760 for my $idata ( @{ $tdata->{'indices'} || [] } ) {
761 my $index = $table->add_index(
762 name => $idata->{'name'},
763 type => uc $idata->{'type'},
764 fields => $idata->{'fields'},
765 ) or die $table->error;
766 }
767
02a21f1a 768 if ( my @options = @{ $tdata->{'table_options'} || [] } ) {
769 $table->options( \@options ) or die $table->error;
770 }
771
f2cf1734 772 for my $cdata ( @{ $tdata->{'constraints'} || [] } ) {
773 my $constraint = $table->add_constraint(
774 name => $cdata->{'name'},
775 type => $cdata->{'type'},
776 fields => $cdata->{'fields'},
777 reference_table => $cdata->{'reference_table'},
778 reference_fields => $cdata->{'reference_fields'},
779 match_type => $cdata->{'match_type'} || '',
780 on_delete => $cdata->{'on_delete_do'},
781 on_update => $cdata->{'on_update_do'},
782 ) or die $table->error;
8ccdeb42 783 }
784 }
785
f62bd16c 786 return 1;
d529894e 787}
788
7891;
790
034ecdec 791# -------------------------------------------------------------------
d529894e 792# Where man is not nature is barren.
793# William Blake
034ecdec 794# -------------------------------------------------------------------
16dc9970 795
d529894e 796=pod
16dc9970 797
798=head1 AUTHOR
799
d529894e 800Ken Y. Clark E<lt>kclark@cpan.orgE<gt>,
8ccdeb42 801Chris Mungall E<lt>cjm@fruitfly.orgE<gt>.
16dc9970 802
803=head1 SEE ALSO
804
8ccdeb42 805perl(1), Parse::RecDescent, SQL::Translator::Schema.
16dc9970 806
807=cut