1 role SQL::Translator::Grammar::MySQL {
2 # -------------------------------------------------------------------
3 # Copyright (C) 2002-2009 SQLFairy Authors
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation; version 2.
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 # -------------------------------------------------------------------
20 method _build_grammar {
23 my ( $database_name, %tables, $table_order, @table_comments, %views,
24 $view_order, %procedures, $proc_order );
29 # The "eofile" rule makes the parser fail if any "statement" rule
30 # fails. Otherwise, the first successful match by a "statement"
31 # won't cause the failure needed to know that the parse, as a whole,
34 startrule : statement(s) eofile {
36 database_name => $database_name,
39 procedures => \%procedures,
56 use : /use/i WORD "$delimiter"
58 $database_name = $item[2];
62 set : /set/i /[^;]+/ "$delimiter"
63 { @table_comments = () }
65 drop : /drop/i TABLE /[^;]+/ "$delimiter"
67 drop : /drop/i WORD(s) "$delimiter"
68 { @table_comments = () }
71 # MySQL strings, unlike common SQL strings, can be double-quoted or
72 # single-quoted, and you can escape the delmiters by doubling (but only the
73 # delimiter) or by backslashing.
75 /'(\\.|''|[^\\\'])*'/ |
77 # For reference, std sql str: /(?:(?:\')(?:[^\']*(?:(?:\'\')[^\']*)*)(?:\'))//
79 nonstring : /[^;\'"]+/
81 statement_body : string | nonstring
83 insert : /insert/i statement_body(s?) "$delimiter"
85 delimiter : /delimiter/i /[\S]+/
86 { $delimiter = $item[2] }
88 empty_statement : "$delimiter"
90 alter : ALTER TABLE table_name alter_specification(s /,/) "$delimiter"
92 my $table_name = $item{'table_name'};
93 die "Cannot ALTER table '$table_name'; it does not exist"
94 unless $tables{ $table_name };
95 for my $definition ( @{ $item[4] } ) {
96 $definition->{'extra'}->{'alter'} = 1;
97 push @{ $tables{ $table_name }{'constraints'} }, $definition;
101 alter_specification : ADD foreign_key_def
102 { $return = $item[2] }
104 create : CREATE /database/i WORD "$delimiter"
105 { @table_comments = () }
107 create : CREATE TEMPORARY(?) TABLE opt_if_not_exists(?) table_name '(' create_definition(s /,/) /(,\s*)?\)/ table_option(s?) "$delimiter"
109 my $table_name = $item{'table_name'};
110 $tables{ $table_name }{'order'} = ++$table_order;
111 $tables{ $table_name }{'table_name'} = $table_name;
113 if ( @table_comments ) {
114 $tables{ $table_name }{'comments'} = [ @table_comments ];
115 @table_comments = ();
119 for my $definition ( @{ $item[7] } ) {
120 if ( $definition->{'supertype'} eq 'field' ) {
121 my $field_name = $definition->{'name'};
122 $tables{ $table_name }{'fields'}{ $field_name } =
123 { %$definition, order => $i };
126 if ( $definition->{'is_primary_key'} ) {
127 push @{ $tables{ $table_name }{'constraints'} },
129 type => 'primary_key',
130 fields => [ $field_name ],
135 elsif ( $definition->{'supertype'} eq 'constraint' ) {
136 push @{ $tables{ $table_name }{'constraints'} }, $definition;
138 elsif ( $definition->{'supertype'} eq 'index' ) {
139 push @{ $tables{ $table_name }{'indices'} }, $definition;
143 if ( my @options = @{ $item{'table_option(s?)'} } ) {
144 for my $option ( @options ) {
145 my ( $key, $value ) = each %$option;
146 if ( $key eq 'comment' ) {
147 push @{ $tables{ $table_name }{'comments'} }, $value;
150 push @{ $tables{ $table_name }{'table_options'} }, $option;
158 opt_if_not_exists : /if not exists/i
160 create : CREATE UNIQUE(?) /(index|key)/i index_name /on/i table_name '(' field_name(s /,/) ')' "$delimiter"
162 @table_comments = ();
163 push @{ $tables{ $item{'table_name'} }{'indices'} },
166 type => $item[2][0] ? 'unique' : 'normal',
172 create : CREATE /trigger/i NAME not_delimiter "$delimiter"
174 @table_comments = ();
177 create : CREATE PROCEDURE NAME not_delimiter "$delimiter"
179 @table_comments = ();
180 my $func_name = $item[3];
182 my $sql = "$item[1] $item[2] $item[3] $item[4]";
184 $procedures{ $func_name }{'order'} = ++$proc_order;
185 $procedures{ $func_name }{'name'} = $func_name;
186 $procedures{ $func_name }{'owner'} = $owner;
187 $procedures{ $func_name }{'sql'} = $sql;
190 PROCEDURE : /procedure/i
193 create : CREATE replace(?) algorithm(?) /view/i NAME not_delimiter "$delimiter"
195 @table_comments = ();
196 my $view_name = $item[5];
197 my $sql = join(q{ }, grep { defined and length } $item[1], $item[2]->[0], $item[3]->[0])
198 . " $item[4] $item[5] $item[6]";
200 # Hack to strip database from function calls in SQL
201 $sql =~ s#`\w+`\.(`\w+`\()##g;
203 $views{ $view_name }{'order'} = ++$view_order;
204 $views{ $view_name }{'name'} = $view_name;
205 $views{ $view_name }{'sql'} = $sql;
208 replace : /or replace/i
210 algorithm : /algorithm/i /=/ WORD
212 $return = "$item[1]=$item[3]";
215 not_delimiter : /.*?(?=$delimiter)/is
217 create_definition : constraint
223 comment : /^\s*(?:#|-{2}).*\n/
225 my $comment = $item[1];
226 $comment =~ s/^\s*(#|--)\s*//;
227 $comment =~ s/\s*$//;
231 comment : /\/\*/ /.*?\*\//s
233 my $comment = $item[2];
234 $comment = substr($comment, 0, -2);
235 $comment =~ s/^\s*|\s*$//g;
239 field_comment : /^\s*(?:#|-{2}).*\n/
241 my $comment = $item[1];
242 $comment =~ s/^\s*(#|--)\s*//;
243 $comment =~ s/\s*$//;
248 field_comment2 : /comment/i /'.*?'/
250 my $comment = $item[2];
258 field : field_comment(s?) field_name data_type field_qualifier(s?) field_comment2(?) reference_definition(?) on_update(?) field_comment(s?)
260 my %qualifiers = map { %$_ } @{ $item{'field_qualifier(s?)'} || [] };
261 if ( my @type_quals = @{ $item{'data_type'}{'qualifiers'} || [] } ) {
262 $qualifiers{ $_ } = 1 for @type_quals;
265 my $null = defined $qualifiers{'not_null'}
266 ? $qualifiers{'not_null'} : 1;
267 delete $qualifiers{'not_null'};
269 my @comments = ( @{ $item[1] }, @{ $item[5] }, @{ $item[8] } );
272 supertype => 'field',
273 name => $item{'field_name'},
274 data_type => $item{'data_type'}{'type'},
275 size => $item{'data_type'}{'size'},
276 list => $item{'data_type'}{'list'},
278 constraints => $item{'reference_definition(?)'},
279 comments => [ @comments ],
285 field_qualifier : not_null
288 null => $item{'not_null'},
292 field_qualifier : default_val
295 default => $item{'default_val'},
299 field_qualifier : auto_inc
302 is_auto_inc => $item{'auto_inc'},
306 field_qualifier : primary_key
309 is_primary_key => $item{'primary_key'},
313 field_qualifier : unsigned
316 is_unsigned => $item{'unsigned'},
320 field_qualifier : /character set/i WORD
323 'CHARACTER SET' => $item[2],
327 field_qualifier : /collate/i WORD
334 field_qualifier : /on update/i CURRENT_TIMESTAMP
337 'ON UPDATE' => $item[2],
341 field_qualifier : /unique/i KEY(?)
348 field_qualifier : KEY
355 reference_definition : /references/i table_name parens_field_list(?) match_type(?) on_delete(?) on_update(?)
358 type => 'foreign_key',
359 reference_table => $item[2],
360 reference_fields => $item[3][0],
361 match_type => $item[4][0],
362 on_delete => $item[5][0],
363 on_update => $item[6][0],
367 match_type : /match full/i { 'full' }
369 /match partial/i { 'partial' }
371 on_delete : /on delete/i reference_option
375 /on update/i 'CURRENT_TIMESTAMP'
378 /on update/i reference_option
381 reference_option: /restrict/i |
399 data_type : WORD parens_value_list(s?) type_qualifier(s?)
402 my $size; # field size, applicable only to non-set fields
403 my $list; # set list, applicable only to sets (duh)
405 if ( uc($type) =~ /^(SET|ENUM)$/ ) {
419 qualifiers => $item[3],
423 parens_field_list : '(' field_name(s /,/) ')'
426 parens_value_list : '(' VALUE(s /,/) ')'
429 type_qualifier : /(BINARY|UNSIGNED|ZEROFILL)/i
434 create_index : /create/i /index/i
436 not_null : /not/i /null/i
442 unsigned : /unsigned/i { $return = 0 }
444 #default_val : /default/i /(?:')?[\s\w\d:.-]*(?:')?/
446 # $item[2] =~ s/'//g;
447 # $return = $item[2];
451 /default/i 'CURRENT_TIMESTAMP'
456 /default/i /'(?:.*?(?:\\'|''))*.*?'|(?:')?[\w\d:.-]*(?:')?/
458 $item[2] =~ s/^\s*'|'\s*$//g;
462 auto_inc : /auto_increment/i { 1 }
464 primary_key : /primary/i /key/i { 1 }
466 constraint : primary_key_def
471 foreign_key_def : foreign_key_def_begin parens_field_list reference_definition
474 supertype => 'constraint',
475 type => 'foreign_key',
478 %{ $item{'reference_definition'} },
482 foreign_key_def_begin : /constraint/i /foreign key/i WORD
483 { $return = $item[3] }
485 /constraint/i NAME /foreign key/i
486 { $return = $item[2] }
488 /constraint/i /foreign key/i
492 { $return = $item[2] }
497 primary_key_def : primary_key index_name(?) '(' name_with_opt_paren(s /,/) ')'
500 supertype => 'constraint',
501 name => $item{'index_name(?)'}[0],
502 type => 'primary_key',
507 unique_key_def : UNIQUE KEY(?) index_name(?) '(' name_with_opt_paren(s /,/) ')'
510 supertype => 'constraint',
511 name => $item{'index_name(?)'}[0],
517 normal_index : KEY index_name(?) '(' name_with_opt_paren(s /,/) ')'
520 supertype => 'index',
522 name => $item{'index_name(?)'}[0],
527 fulltext_index : /fulltext/i KEY(?) index_name(?) '(' name_with_opt_paren(s /,/) ')'
530 supertype => 'index',
532 name => $item{'index_name(?)'}[0],
537 spatial_index : /spatial/i KEY(?) index_name(?) '(' name_with_opt_paren(s /,/) ')'
540 supertype => 'index',
542 name => $item{'index_name(?)'}[0],
547 name_with_opt_paren : NAME parens_value_list(s?)
548 { $item[2][0] ? "$item[1]($item[2][0][0])" : $item[1] }
552 KEY : /key/i | /index/i
554 table_option : /comment/i /=/ /'.*?'/
556 my $comment = $item[3];
559 $return = { comment => $comment };
561 | /(default )?(charset|character set)/i /\s*=?\s*/ WORD
563 $return = { 'CHARACTER SET' => $item[3] };
567 $return = { 'COLLATE' => $item[2] }
569 | /union/i /\s*=\s*/ '(' table_name(s /,/) ')'
571 $return = { $item[1] => $item[4] };
573 | WORD /\s*=\s*/ MAYBE_QUOTED_WORD
575 $return = { $item[1] => $item[3] };
578 MAYBE_QUOTED_WORD: /\w+/
592 TEMPORARY : /temporary/i
606 NAME : BACKTICK /[^`]+/ BACKTICK
608 | DOUBLE_QUOTE /[^"]+/ DOUBLE_QUOTE
613 VALUE : /[-+]?\.?\d+(?:[eE]\d+)?/
617 # remove leading/trailing quotes
619 $val =~ s/^['"]|['"]$//g;
625 CURRENT_TIMESTAMP : /current_timestamp(\(\))?/i
627 { 'CURRENT_TIMESTAMP' }