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