Added "auto-dia.pl" script for generating ER diagrams.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / MySQL.pm
CommitLineData
16dc9970 1package SQL::Translator::Parser::MySQL;
2
49e1eb70 3# -------------------------------------------------------------------
8d0f3086 4# $Id: MySQL.pm,v 1.10 2003-01-29 13:28:28 dlc Exp $
49e1eb70 5# -------------------------------------------------------------------
abfa405a 6# Copyright (C) 2003 Ken Y. Clark <kclark@cpan.org>,
7# darren chamberlain <darren@cpan.org>,
8# Chris Mungall <cjm@fruitfly.org>
077ebf34 9#
10# This program is free software; you can redistribute it and/or
11# modify it under the terms of the GNU General Public License as
12# published by the Free Software Foundation; version 2.
13#
14# This program is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17# General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22# 02111-1307 USA
23# -------------------------------------------------------------------
16dc9970 24
d529894e 25=head1 NAME
26
27SQL::Translator::Parser::MySQL - parser for MySQL
28
29=head1 SYNOPSIS
30
31 use SQL::Translator;
32 use SQL::Translator::Parser::MySQL;
33
34 my $translator = SQL::Translator->new;
35 $translator->parser("SQL::Translator::Parser::MySQL");
36
37=head1 DESCRIPTION
38
39The grammar is influenced heavily by Tim Bunce's "mysql2ora" grammar.
40
41=cut
42
16dc9970 43use strict;
d529894e 44use vars qw[ $DEBUG $VERSION $GRAMMAR @EXPORT_OK ];
8d0f3086 45$VERSION = sprintf "%d.%02d", q$Revision: 1.10 $ =~ /(\d+)\.(\d+)/;
46$DEBUG = 0 unless defined $DEBUG;
077ebf34 47
d529894e 48use Data::Dumper;
077ebf34 49use Parse::RecDescent;
50use Exporter;
51use base qw(Exporter);
52
53@EXPORT_OK = qw(parse);
54
d529894e 55# Enable warnings within the Parse::RecDescent module.
56$::RD_ERRORS = 1; # Make sure the parser dies when it encounters an error
57$::RD_WARN = 1; # Enable warnings. This will warn on unused rules &c.
58$::RD_HINT = 1; # Give out hints to help fix problems.
59
077ebf34 60my $parser; # should we do this? There's no programmic way to
61 # change the grammar, so I think this is safe.
16dc9970 62
d529894e 63$GRAMMAR = q!
64
65{ our ( %tables, $table_order ) }
66
67startrule : statement(s) { \%tables }
68
69statement : comment
61745327 70 | drop
d529894e 71 | create
72 | <error>
73
61745327 74drop : /drop/i WORD(s) ';'
75
d529894e 76create : create_table table_name '(' create_definition(s /,/) ')' table_option(s?) ';'
77 {
78 my $table_name = $item{'table_name'};
79 $tables{ $table_name }{'order'} = ++$table_order;
80 $tables{ $table_name }{'table_name'} = $table_name;
81
61745327 82 my $i = 1;
d529894e 83 for my $definition ( @{ $item[4] } ) {
84 if ( $definition->{'type'} eq 'field' ) {
85 my $field_name = $definition->{'name'};
86 $tables{ $table_name }{'fields'}{ $field_name } =
87 { %$definition, order => $i };
88 $i++;
89
90 if ( $definition->{'is_primary_key'} ) {
91 push @{ $tables{ $table_name }{'indices'} },
92 {
93 type => 'primary_key',
94 fields => [ $field_name ],
16dc9970 95 }
d529894e 96 ;
97 }
dd2ef5ae 98 }
d529894e 99 else {
100 push @{ $tables{ $table_name }{'indices'} },
101 $definition;
dd2ef5ae 102 }
d529894e 103 }
dd2ef5ae 104
d529894e 105 for my $opt ( @{ $item{'table_option'} } ) {
106 if ( my ( $key, $val ) = each %$opt ) {
107 $tables{ $table_name }{'table_options'}{ $key } = $val;
dd2ef5ae 108 }
d529894e 109 }
110 }
dd2ef5ae 111
d529894e 112create : /CREATE/i unique(?) /(INDEX|KEY)/i index_name /on/i table_name '(' field_name(s /,/) ')' ';'
113 {
114 push @{ $tables{ $item{'table_name'} }{'indices'} },
115 {
116 name => $item[4],
117 type => $item[2] ? 'unique' : 'normal',
118 fields => $item[8],
dd2ef5ae 119 }
d529894e 120 ;
121 }
dd2ef5ae 122
d529894e 123create_definition : index
124 | field
125 | <error>
126
127comment : /^\s*(?:#|-{2}).*\n/
128
129blank : /\s*/
130
131field : field_name data_type field_qualifier(s?)
132 {
133 my %qualifiers = map { %$_ } @{ $item{'field_qualifier'} || [] };
134 my $null = defined $item{'not_null'} ? $item{'not_null'} : 1;
135 delete $qualifiers{'not_null'};
136 if ( my @type_quals = @{ $item{'data_type'}{'qualifiers'} || [] } ) {
137 $qualifiers{ $_ } = 1 for @type_quals;
138 }
139
140 $return = {
141 type => 'field',
142 name => $item{'field_name'},
143 data_type => $item{'data_type'}{'type'},
144 size => $item{'data_type'}{'size'},
145 list => $item{'data_type'}{'list'},
146 null => $null,
147 %qualifiers,
148 }
149 }
150 | <error>
dd2ef5ae 151
d529894e 152field_qualifier : not_null
153 {
154 $return = {
155 null => $item{'not_null'},
156 }
157 }
16dc9970 158
d529894e 159field_qualifier : default_val
160 {
161 $return = {
162 default => $item{'default_val'},
163 }
164 }
16dc9970 165
d529894e 166field_qualifier : auto_inc
167 {
168 $return = {
169 is_auto_inc => $item{'auto_inc'},
170 }
171 }
16dc9970 172
d529894e 173field_qualifier : primary_key
174 {
175 $return = {
176 is_primary_key => $item{'primary_key'},
177 }
178 }
16dc9970 179
d529894e 180field_qualifier : unsigned
181 {
182 $return = {
183 is_unsigned => $item{'unsigned'},
184 }
185 }
16dc9970 186
d529894e 187index : primary_key_index
188 | unique_index
189 | normal_index
190
191table_name : WORD
192
193field_name : WORD
194
195index_name : WORD
196
197data_type : WORD parens_value_list(s?) type_qualifier(s?)
198 {
199 my $type = $item[1];
200 my $size; # field size, applicable only to non-set fields
201 my $list; # set list, applicable only to sets (duh)
202
44fcd0b5 203 if ( uc($type) =~ /^(SET|ENUM)$/ ) {
d529894e 204 $size = undef;
205 $list = $item[2][0];
206 }
207 else {
208 $size = $item[2][0];
209 $list = [];
210 }
211
212 $return = {
213 type => $type,
214 size => $size,
215 list => $list,
216 qualifiers => $item[3],
217 }
218 }
16dc9970 219
d529894e 220parens_value_list : '(' VALUE(s /,/) ')'
221 { $item[2] }
16dc9970 222
d529894e 223type_qualifier : /(BINARY|UNSIGNED|ZEROFILL)/i
224 { lc $item[1] }
16dc9970 225
d529894e 226field_type : WORD
16dc9970 227
d529894e 228field_size : '(' num_range ')' { $item{'num_range'} }
16dc9970 229
d529894e 230num_range : DIGITS ',' DIGITS
231 { $return = $item[1].','.$item[3] }
232 | DIGITS
233 { $return = $item[1] }
dd2ef5ae 234
d529894e 235create_table : /create/i /table/i
16dc9970 236
d529894e 237create_index : /create/i /index/i
dd2ef5ae 238
d529894e 239not_null : /not/i /null/i { $return = 0 }
16dc9970 240
d529894e 241unsigned : /unsigned/i { $return = 0 }
16dc9970 242
d529894e 243default_val : /default/i /(?:')?[\w\d.-]*(?:')?/
244 {
245 $item[2] =~ s/'//g;
246 $return = $item[2];
247 }
16dc9970 248
d529894e 249auto_inc : /auto_increment/i { 1 }
16dc9970 250
d529894e 251primary_key : /primary/i /key/i { 1 }
16dc9970 252
d529894e 253primary_key_index : primary_key index_name(?) '(' field_name(s /,/) ')'
254 {
255 $return = {
256 name => $item{'index_name'}[0],
257 type => 'primary_key',
258 fields => $item[4],
259 }
260 }
16dc9970 261
d529894e 262normal_index : key index_name(?) '(' name_with_opt_paren(s /,/) ')'
263 {
264 $return = {
265 name => $item{'index_name'}[0],
266 type => 'normal',
267 fields => $item[4],
268 }
269 }
16dc9970 270
d529894e 271unique_index : unique key(?) index_name(?) '(' name_with_opt_paren(s /,/) ')'
272 {
273 $return = {
274 name => $item{'index_name'}[0],
275 type => 'unique',
276 fields => $item[5],
277 }
278 }
16dc9970 279
d529894e 280name_with_opt_paren : NAME parens_value_list(s?)
281 { $item[2][0] ? "$item[1]($item[2][0][0])" : $item[1] }
16dc9970 282
d529894e 283unique : /unique/i { 1 }
16dc9970 284
d529894e 285key : /key/i | /index/i
16dc9970 286
44fcd0b5 287table_option : /[^\s;]*/
d529894e 288 {
289 $return = { split /=/, $item[1] }
290 }
16dc9970 291
d529894e 292WORD : /\w+/
16dc9970 293
d529894e 294DIGITS : /\d+/
16dc9970 295
d529894e 296COMMA : ','
16dc9970 297
d529894e 298NAME : "`" /\w+/ "`"
299 { $item[2] }
300 | /\w+/
301 { $item[1] }
16dc9970 302
d529894e 303VALUE : /[-+]?\.?\d+(?:[eE]\d+)?/
304 { $item[1] }
305 | /'.*?'/ # XXX doesn't handle embedded quotes
306 { $item[1] }
307 | /NULL/
308 { 'NULL' }
16dc9970 309
d529894e 310!;
16dc9970 311
d529894e 312# -------------------------------------------------------------------
313sub parse {
314 my ( $translator, $data ) = @_;
315 $parser ||= Parse::RecDescent->new($GRAMMAR);
077ebf34 316
d529894e 317 $::RD_TRACE = $translator->trace ? 1 : undef;
318 $DEBUG = $translator->debug;
319
320 unless (defined $parser) {
321 return $translator->error("Error instantiating Parse::RecDescent ".
322 "instance: Bad grammer");
323 }
324
325 my $result = $parser->startrule($data);
326 die "Parse failed.\n" unless defined $result;
327 warn Dumper($result) if $DEBUG;
328 return $result;
329}
330
3311;
332
333#-----------------------------------------------------
334# Where man is not nature is barren.
335# William Blake
336#-----------------------------------------------------
16dc9970 337
d529894e 338=pod
16dc9970 339
340=head1 AUTHOR
341
d529894e 342Ken Y. Clark E<lt>kclark@cpan.orgE<gt>,
343Chris Mungall
16dc9970 344
345=head1 SEE ALSO
346
d529894e 347perl(1), Parse::RecDescent.
16dc9970 348
349=cut