001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.sequencer.ddl.dialect.mysql;
017
018import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DDL_EXPRESSION;
019import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DDL_ORIGINAL_EXPRESSION;
020import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DDL_START_CHAR_INDEX;
021import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DDL_START_COLUMN_NUMBER;
022import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DDL_START_LINE_NUMBER;
023import static org.modeshape.sequencer.ddl.StandardDdlLexicon.NEW_NAME;
024import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_ALGORITHM_STATEMENT;
025import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_DATABASE_STATEMENT;
026import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_DEFINER_STATEMENT;
027import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_EVENT_STATEMENT;
028import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_FUNCTION_STATEMENT;
029import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_LOGFILE_GROUP_STATEMENT;
030import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_PROCEDURE_STATEMENT;
031import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_SCHEMA_STATEMENT;
032import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_SERVER_STATEMENT;
033import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_TABLESPACE_STATEMENT;
034import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_ALTER_VIEW_STATEMENT;
035import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_CREATE_DEFINER_STATEMENT;
036import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_CREATE_EVENT_STATEMENT;
037import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_CREATE_FUNCTION_STATEMENT;
038import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_CREATE_INDEX_STATEMENT;
039import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_CREATE_PROCEDURE_STATEMENT;
040import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_CREATE_SERVER_STATEMENT;
041import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_CREATE_TABLESPACE_STATEMENT;
042import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_CREATE_TRIGGER_STATEMENT;
043import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_DROP_DATABASE_STATEMENT;
044import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_DROP_EVENT_STATEMENT;
045import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_DROP_FUNCTION_STATEMENT;
046import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_DROP_INDEX_STATEMENT;
047import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_DROP_LOGFILE_GROUP_STATEMENT;
048import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_DROP_PROCEDURE_STATEMENT;
049import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_DROP_SERVER_STATEMENT;
050import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_DROP_TABLESPACE_STATEMENT;
051import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_DROP_TRIGGER_STATEMENT;
052import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_RENAME_DATABASE_STATEMENT;
053import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_RENAME_SCHEMA_STATEMENT;
054import static org.modeshape.sequencer.ddl.dialect.mysql.MySqlDdlLexicon.TYPE_RENAME_TABLE_STATEMENT;
055import java.util.ArrayList;
056import java.util.List;
057import org.modeshape.common.text.ParsingException;
058import org.modeshape.common.text.TokenStream;
059import org.modeshape.sequencer.ddl.DdlTokenStream;
060import org.modeshape.sequencer.ddl.StandardDdlParser;
061import org.modeshape.sequencer.ddl.datatype.DataType;
062import org.modeshape.sequencer.ddl.datatype.DataTypeParser;
063import org.modeshape.sequencer.ddl.node.AstNode;
064
065/**
066 * MySql-specific DDL Parser. Includes custom data types as well as custom DDL statements.
067 */
068public class MySqlDdlParser extends StandardDdlParser implements MySqlDdlConstants, MySqlDdlConstants.MySqlStatementStartPhrases {
069    /**
070     * The MySQL parser identifier.
071     */
072    public static final String ID = "MYSQL";
073
074    static List<String[]> mysqlDataTypeStrings = new ArrayList<String[]>();
075    /*
076    * ===========================================================================================================================
077    * Data Definition Statements
078        ALTER [DATABASE | EVENT | FUNCTION | SERVER | TABLE | VIEW]
079        CREATE [DATABASE | EVENT | FUNCTION | INDEX | PROCEDURE | SERVER | TABLE | TRIGGER | VIEW]
080        DROP [DATABASE | EVENT | FUNCTION | INDEX | PROCEDURE | SERVER | TABLE | TRIGGER | VIEW]
081        RENAME TABLE
082    */
083
084    /*
085    * ===========================================================================================================================
086    * CREATE TABLE
087    
088    CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
089    (create_definition,...)
090    [table_options]
091    [partition_options]
092
093    Or:
094    
095    CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
096        [(create_definition,...)]
097        [table_options]
098        [partition_options]
099        select_statement
100    
101    Or:
102    
103    CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
104        { LIKE old_tbl_name | (LIKE old_tbl_name) }
105    
106    create_definition:
107        col_name column_definition
108      | [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...)
109          [index_option] ...
110      | {INDEX|KEY} [index_name] [index_type] (index_col_name,...)
111          [index_option] ...
112      | [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY]
113          [index_name] [index_type] (index_col_name,...)
114          [index_option] ...
115      | {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (index_col_name,...)
116          [index_option] ...
117      | [CONSTRAINT [symbol]] FOREIGN KEY
118          [index_name] (index_col_name,...) reference_definition
119      | CHECK (expr)
120    
121    column_definition:
122        data_type [NOT NULL | NULL] [DEFAULT default_value]
123          [AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY]
124          [COMMENT 'string']
125          [COLUMN_FORMAT {FIXED|DYNAMIC|DEFAULT}]
126          [STORAGE {DISK|MEMORY|DEFAULT}]
127          [reference_definition]
128    
129    index_col_name:
130        col_name [(length)] [ASC | DESC]
131    
132    index_type:
133        USING {BTREE | HASH | RTREE}
134    
135    index_option:
136        KEY_BLOCK_SIZE [=] value
137      | index_type
138      | WITH PARSER parser_name
139    
140    reference_definition:
141        REFERENCES tbl_name (index_col_name,...)
142          [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
143          [ON DELETE reference_option]
144          [ON UPDATE reference_option]
145    
146    reference_option:
147        RESTRICT | CASCADE | SET NULL | NO ACTION
148    
149    table_options:
150        table_option [[,] table_option] ...
151    
152    table_option:
153        ENGINE [=] engine_name
154      | AUTO_INCREMENT [=] value
155      | AVG_ROW_LENGTH [=] value
156      | [DEFAULT] CHARACTER SET [=] charset_name
157      | CHECKSUM [=] {0 | 1}
158      | [DEFAULT] COLLATE [=] collation_name
159      | COMMENT [=] 'string'
160      | CONNECTION [=] 'connect_string'
161      | DATA DIRECTORY [=] 'absolute path to directory'
162      | DELAY_KEY_WRITE [=] {0 | 1}
163      | INDEX DIRECTORY [=] 'absolute path to directory'
164      | INSERT_METHOD [=] { NO | FIRST | LAST }
165      | KEY_BLOCK_SIZE [=] value
166      | MAX_ROWS [=] value
167      | MIN_ROWS [=] value
168      | PACK_KEYS [=] {0 | 1 | DEFAULT}
169      | PASSWORD [=] 'string'
170      | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT}
171      | TABLESPACE tablespace_name [STORAGE {DISK|MEMORY|DEFAULT}]
172      | UNION [=] (tbl_name[,tbl_name]...)
173    
174    partition_options:
175        PARTITION BY
176            { [LINEAR] HASH(expr)
177            | [LINEAR] KEY(column_list)
178            | RANGE(expr)
179            | LIST(expr) }
180        [PARTITIONS num]
181        [SUBPARTITION BY
182            { [LINEAR] HASH(expr)
183            | [LINEAR] KEY(column_list) }
184          [SUBPARTITIONS num]
185        ]
186        [(partition_definition [, partition_definition] ...)]
187    
188    partition_definition:
189        PARTITION partition_name
190            [VALUES {LESS THAN {(expr) | MAXVALUE} | IN (value_list)}]
191            [[STORAGE] ENGINE [=] engine_name]
192            [COMMENT [=] 'comment_text' ]
193            [DATA DIRECTORY [=] 'data_dir']        
194            [INDEX DIRECTORY [=] 'index_dir']
195            [MAX_ROWS [=] max_number_of_rows]
196            [MIN_ROWS [=] min_number_of_rows]
197            [TABLESPACE [=] tablespace_name]
198            [NODEGROUP [=] node_group_id]
199            [(subpartition_definition [, subpartition_definition] ...)]
200    
201    subpartition_definition:
202        SUBPARTITION logical_name
203            [[STORAGE] ENGINE [=] engine_name]
204            [COMMENT [=] 'comment_text' ]
205            [DATA DIRECTORY [=] 'data_dir']
206            [INDEX DIRECTORY [=] 'index_dir']
207            [MAX_ROWS [=] max_number_of_rows]
208            [MIN_ROWS [=] min_number_of_rows]
209            [TABLESPACE [=] tablespace_name]
210            [NODEGROUP [=] node_group_id]
211    
212    select_statement:
213        [IGNORE | REPLACE] [AS] SELECT ...   (Some legal select statement)
214
215    
216    * ===========================================================================================================================
217    */
218    private static final String TERMINATOR = DEFAULT_TERMINATOR;
219
220    public MySqlDdlParser() {
221        initialize();
222    }
223
224    /**
225     * {@inheritDoc}
226     * 
227     * @see org.modeshape.sequencer.ddl.StandardDdlParser#getId()
228     */
229    @Override
230    public String getId() {
231        return ID;
232    }
233
234    private void initialize() {
235        setDatatypeParser(new MySqlDataTypeParser());
236
237        setDoUseTerminator(true);
238
239        setTerminator(TERMINATOR);
240
241        mysqlDataTypeStrings.addAll(MySqlDataTypes.CUSTOM_DATATYPE_START_PHRASES);
242    }
243
244    /**
245     * {@inheritDoc}
246     * 
247     * @see org.modeshape.sequencer.ddl.StandardDdlParser#initializeTokenStream(org.modeshape.sequencer.ddl.DdlTokenStream)
248     */
249    @Override
250    protected void initializeTokenStream( DdlTokenStream tokens ) {
251        super.initializeTokenStream(tokens);
252        tokens.registerKeyWords(CUSTOM_KEYWORDS);
253        tokens.registerKeyWords(MySqlDataTypes.CUSTOM_DATATYPE_START_WORDS);
254        tokens.registerStatementStartPhrase(ALTER_PHRASES);
255        tokens.registerStatementStartPhrase(CREATE_PHRASES);
256        tokens.registerStatementStartPhrase(DROP_PHRASES);
257        tokens.registerStatementStartPhrase(MISC_PHRASES);
258    }
259
260    /**
261     * {@inheritDoc}
262     * 
263     * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseCreateStatement(org.modeshape.sequencer.ddl.DdlTokenStream,
264     *      org.modeshape.sequencer.ddl.node.AstNode)
265     */
266    @Override
267    protected AstNode parseCreateStatement( DdlTokenStream tokens,
268                                            AstNode parentNode ) throws ParsingException {
269        assert tokens != null;
270        assert parentNode != null;
271
272        if (tokens.matches(STMT_CREATE_INDEX)) {
273            return parseStatement(tokens, MySqlStatementStartPhrases.STMT_CREATE_INDEX, parentNode, TYPE_CREATE_INDEX_STATEMENT);
274        } else if (tokens.matches(STMT_CREATE_UNIQUE_INDEX)) {
275            return parseStatement(tokens,
276                                  MySqlStatementStartPhrases.STMT_CREATE_UNIQUE_INDEX,
277                                  parentNode,
278                                  TYPE_CREATE_INDEX_STATEMENT);
279        } else if (tokens.matches(STMT_CREATE_FUNCTION)) {
280            return parseStatement(tokens,
281                                  MySqlStatementStartPhrases.STMT_CREATE_FUNCTION,
282                                  parentNode,
283                                  TYPE_CREATE_FUNCTION_STATEMENT);
284        } else if (tokens.matches(STMT_CREATE_PROCEDURE)) {
285            return parseStatement(tokens,
286                                  MySqlStatementStartPhrases.STMT_CREATE_PROCEDURE,
287                                  parentNode,
288                                  TYPE_CREATE_PROCEDURE_STATEMENT);
289        } else if (tokens.matches(STMT_CREATE_SERVER)) {
290            return parseStatement(tokens, MySqlStatementStartPhrases.STMT_CREATE_SERVER, parentNode, TYPE_CREATE_SERVER_STATEMENT);
291        } else if (tokens.matches(STMT_CREATE_TRIGGER)) {
292            return parseStatement(tokens,
293                                  MySqlStatementStartPhrases.STMT_CREATE_TRIGGER,
294                                  parentNode,
295                                  TYPE_CREATE_TRIGGER_STATEMENT);
296        } else if (tokens.matches(STMT_CREATE_EVENT)) {
297            return parseStatement(tokens, MySqlStatementStartPhrases.STMT_CREATE_EVENT, parentNode, TYPE_CREATE_EVENT_STATEMENT);
298        } else if (tokens.matches(STMT_CREATE_TABLESPACE)) {
299            return parseStatement(tokens,
300                                  MySqlStatementStartPhrases.STMT_CREATE_TABLESPACE,
301                                  parentNode,
302                                  TYPE_CREATE_TABLESPACE_STATEMENT);
303        } else if (tokens.matches(STMT_CREATE_DEFINER)) {
304            return parseStatement(tokens,
305                                  MySqlStatementStartPhrases.STMT_CREATE_DEFINER,
306                                  parentNode,
307                                  TYPE_CREATE_DEFINER_STATEMENT);
308        }
309
310        return super.parseCreateStatement(tokens, parentNode);
311    }
312
313    /**
314     * {@inheritDoc}
315     * 
316     * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseAlterStatement(org.modeshape.sequencer.ddl.DdlTokenStream,
317     *      org.modeshape.sequencer.ddl.node.AstNode)
318     */
319    @Override
320    protected AstNode parseAlterStatement( DdlTokenStream tokens,
321                                           AstNode parentNode ) throws ParsingException {
322        assert tokens != null;
323        assert parentNode != null;
324
325        if (tokens.matches(STMT_ALTER_ALGORITHM)) {
326            return parseStatement(tokens, STMT_ALTER_ALGORITHM, parentNode, TYPE_ALTER_ALGORITHM_STATEMENT);
327        } else if (tokens.matches(STMT_ALTER_DATABASE)) {
328            return parseStatement(tokens, STMT_ALTER_DATABASE, parentNode, TYPE_ALTER_DATABASE_STATEMENT);
329        } else if (tokens.matches(STMT_ALTER_DEFINER)) {
330            return parseStatement(tokens, STMT_ALTER_DEFINER, parentNode, TYPE_ALTER_DEFINER_STATEMENT);
331        } else if (tokens.matches(STMT_ALTER_EVENT)) {
332            return parseStatement(tokens, STMT_ALTER_EVENT, parentNode, TYPE_ALTER_EVENT_STATEMENT);
333        } else if (tokens.matches(STMT_ALTER_FUNCTION)) {
334            return parseStatement(tokens, STMT_ALTER_FUNCTION, parentNode, TYPE_ALTER_FUNCTION_STATEMENT);
335        } else if (tokens.matches(STMT_ALTER_LOGFILE_GROUP)) {
336            return parseStatement(tokens, STMT_ALTER_LOGFILE_GROUP, parentNode, TYPE_ALTER_LOGFILE_GROUP_STATEMENT);
337        } else if (tokens.matches(STMT_ALTER_PROCEDURE)) {
338            return parseStatement(tokens, STMT_ALTER_PROCEDURE, parentNode, TYPE_ALTER_PROCEDURE_STATEMENT);
339        } else if (tokens.matches(STMT_ALTER_SCHEMA)) {
340            return parseStatement(tokens, STMT_ALTER_SCHEMA, parentNode, TYPE_ALTER_SCHEMA_STATEMENT);
341        } else if (tokens.matches(STMT_ALTER_SERVER)) {
342            return parseStatement(tokens, STMT_ALTER_SERVER, parentNode, TYPE_ALTER_SERVER_STATEMENT);
343        } else if (tokens.matches(STMT_ALTER_TABLESPACE)) {
344            return parseStatement(tokens, STMT_ALTER_TABLESPACE, parentNode, TYPE_ALTER_TABLESPACE_STATEMENT);
345        } else if (tokens.matches(STMT_ALTER_SQL_SECURITY)) {
346            return parseStatement(tokens, STMT_ALTER_SQL_SECURITY, parentNode, TYPE_ALTER_VIEW_STATEMENT);
347        } else if (tokens.matches(STMT_ALTER_IGNORE_TABLE) || tokens.matches(STMT_ALTER_ONLINE_TABLE)
348                   || tokens.matches(STMT_ALTER_ONLINE_IGNORE_TABLE) || tokens.matches(STMT_ALTER_OFFLINE_TABLE)
349                   || tokens.matches(STMT_ALTER_OFFLINE_IGNORE_TABLE)) {
350            return parseAlterTableStatement(tokens, parentNode);
351        }
352
353        return super.parseAlterStatement(tokens, parentNode);
354    }
355
356    /**
357     * {@inheritDoc}
358     * 
359     * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseAlterTableStatement(org.modeshape.sequencer.ddl.DdlTokenStream,
360     *      org.modeshape.sequencer.ddl.node.AstNode)
361     */
362    @Override
363    protected AstNode parseAlterTableStatement( DdlTokenStream tokens,
364                                                AstNode parentNode ) throws ParsingException {
365        assert tokens != null;
366        assert parentNode != null;
367        // TODO:
368        //
369        /*
370         * 
371
372        ALTER [ONLINE | OFFLINE] [IGNORE] TABLE tbl_name
373            alter_specification [, alter_specification] ...
374
375            alter_specification:
376                table_options
377              | ADD [COLUMN] col_name column_definition
378                    [FIRST | AFTER col_name ]
379              | ADD [COLUMN] (col_name column_definition,...)
380              | ADD {INDEX|KEY} [index_name]
381                    [index_type] (index_col_name,...) [index_option] ...
382              | ADD [CONSTRAINT [symbol]] PRIMARY KEY
383                    [index_type] (index_col_name,...) [index_option] ...
384              | ADD [CONSTRAINT [symbol]]
385                    UNIQUE [INDEX|KEY] [index_name]
386                    [index_type] (index_col_name,...) [index_option] ...
387              | ADD FULLTEXT [INDEX|KEY] [index_name]
388                    (index_col_name,...) [index_option] ...
389              | ADD SPATIAL [INDEX|KEY] [index_name]
390                    (index_col_name,...) [index_option] ...
391              | ADD [CONSTRAINT [symbol]]
392                    FOREIGN KEY [index_name] (index_col_name,...)
393                    reference_definition
394              | ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT}
395              | CHANGE [COLUMN] old_col_name new_col_name column_definition
396                    [FIRST|AFTER col_name]
397              | MODIFY [COLUMN] col_name column_definition
398                    [FIRST | AFTER col_name]
399              | DROP [COLUMN] col_name
400              | DROP PRIMARY KEY
401              | DROP {INDEX|KEY} index_name
402              | DROP FOREIGN KEY fk_symbol
403              | DISABLE KEYS
404              | ENABLE KEYS
405              | RENAME [TO] new_tbl_name
406              | ORDER BY col_name [, col_name] ...
407              | CONVERT TO CHARACTER SET charset_name [COLLATE collation_name]
408              | [DEFAULT] CHARACTER SET [=] charset_name [COLLATE [=] collation_name]
409              | DISCARD TABLESPACE
410              | IMPORT TABLESPACE
411              | partition_options
412              | ADD PARTITION (partition_definition)
413              | DROP PARTITION partition_names
414              | COALESCE PARTITION number
415              | REORGANIZE PARTITION [partition_names INTO (partition_definitions)]
416              | ANALYZE PARTITION partition_names
417              | CHECK PARTITION partition_names
418              | OPTIMIZE PARTITION partition_names
419              | REBUILD PARTITION partition_names
420              | REPAIR PARTITION partition_names
421              | REMOVE PARTITIONING
422         */
423
424        return super.parseAlterTableStatement(tokens, parentNode);
425    }
426
427    /**
428     * {@inheritDoc}
429     * 
430     * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseCustomStatement(org.modeshape.sequencer.ddl.DdlTokenStream,
431     *      org.modeshape.sequencer.ddl.node.AstNode)
432     */
433    @Override
434    protected AstNode parseCustomStatement( DdlTokenStream tokens,
435                                            AstNode parentNode ) throws ParsingException {
436        assert tokens != null;
437        assert parentNode != null;
438
439        if (tokens.matches(STMT_RENAME_DATABASE)) {
440            markStartOfStatement(tokens);
441
442            // RENAME DATABASE db_name TO new_db_name;
443            tokens.consume(STMT_RENAME_DATABASE);
444            String oldName = parseName(tokens);
445            tokens.consume("TO");
446            AstNode node = nodeFactory().node(oldName, parentNode, TYPE_RENAME_DATABASE_STATEMENT);
447            String newName = parseName(tokens);
448            node.setProperty(NEW_NAME, newName);
449
450            markEndOfStatement(tokens, node);
451            return node;
452        } else if (tokens.matches(STMT_RENAME_SCHEMA)) {
453            markStartOfStatement(tokens);
454
455            // RENAME SCHEMA schema_name TO new_schema_name;
456            tokens.consume(STMT_RENAME_SCHEMA);
457            String oldName = parseName(tokens);
458            tokens.consume("TO");
459            AstNode node = nodeFactory().node(oldName, parentNode, TYPE_RENAME_SCHEMA_STATEMENT);
460            String newName = parseName(tokens);
461            node.setProperty(NEW_NAME, newName);
462
463            markEndOfStatement(tokens, node);
464            return node;
465        } else if (tokens.matches(STMT_RENAME_TABLE)) {
466            markStartOfStatement(tokens);
467
468            // RENAME TABLE old_table TO tmp_table,
469            // new_table TO old_table,
470            // tmp_table TO new_table;
471            tokens.consume(STMT_RENAME_TABLE);
472
473            String oldName = parseName(tokens);
474            tokens.consume("TO");
475            String newName = parseName(tokens);
476
477            AstNode node = nodeFactory().node(oldName, parentNode, TYPE_RENAME_TABLE_STATEMENT);
478            node.setProperty(NEW_NAME, newName);
479
480            // IF NOT MULTIPLE RENAMES, FINISH AND RETURN
481            if (!tokens.matches(COMMA)) {
482                markEndOfStatement(tokens, node);
483                return node;
484            }
485
486            // Assume multiple renames
487
488            // Create list of nodes so we can re-set the expression of each to reflect ONE rename.
489            List<AstNode> nodes = new ArrayList<AstNode>();
490            nodes.add(node);
491
492            while (tokens.matches(COMMA)) {
493                tokens.consume(COMMA);
494                oldName = parseName(tokens);
495                tokens.consume("TO");
496                newName = parseName(tokens);
497                node = nodeFactory().node(oldName, parentNode, TYPE_RENAME_TABLE_STATEMENT);
498                node.setProperty(NEW_NAME, newName);
499                nodes.add(node);
500            }
501
502            markEndOfStatement(tokens, nodes.get(0));
503
504            String originalExpression = (String)nodes.get(0).getProperty(DDL_EXPRESSION);
505            Object startLineNumber = nodes.get(0).getProperty(DDL_START_LINE_NUMBER);
506            Object startColumnNumber = nodes.get(0).getProperty(DDL_START_COLUMN_NUMBER);
507            Object startCharIndex = nodes.get(0).getProperty(DDL_START_CHAR_INDEX);
508
509            for (AstNode nextNode : nodes) {
510                oldName = nextNode.getName();
511                newName = (String)nextNode.getProperty(NEW_NAME);
512                String express = "RENAME TABLE" + SPACE + oldName + SPACE + "TO" + SPACE + newName + SEMICOLON;
513                nextNode.setProperty(DDL_EXPRESSION, express);
514                nextNode.setProperty(DDL_ORIGINAL_EXPRESSION, originalExpression);
515                nextNode.setProperty(DDL_START_LINE_NUMBER, startLineNumber);
516                nextNode.setProperty(DDL_START_COLUMN_NUMBER, startColumnNumber);
517                nextNode.setProperty(DDL_START_CHAR_INDEX, startCharIndex);
518            }
519
520            return nodes.get(0);
521        }
522
523        return super.parseCustomStatement(tokens, parentNode);
524    }
525
526    /**
527     * {@inheritDoc}
528     * 
529     * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseDropStatement(org.modeshape.sequencer.ddl.DdlTokenStream,
530     *      org.modeshape.sequencer.ddl.node.AstNode)
531     */
532    @Override
533    protected AstNode parseDropStatement( DdlTokenStream tokens,
534                                          AstNode parentNode ) throws ParsingException {
535        assert tokens != null;
536        assert parentNode != null;
537
538        if (tokens.matches(STMT_DROP_DATABASE)) {
539            return parseStatement(tokens, STMT_DROP_DATABASE, parentNode, TYPE_DROP_DATABASE_STATEMENT);
540        } else if (tokens.matches(STMT_DROP_EVENT)) {
541            return parseStatement(tokens, STMT_DROP_EVENT, parentNode, TYPE_DROP_EVENT_STATEMENT);
542        } else if (tokens.matches(STMT_DROP_FUNCTION)) {
543            return parseStatement(tokens, STMT_DROP_FUNCTION, parentNode, TYPE_DROP_FUNCTION_STATEMENT);
544        } else if (tokens.matches(STMT_DROP_INDEX)) {
545            return parseStatement(tokens, STMT_DROP_INDEX, parentNode, TYPE_DROP_INDEX_STATEMENT);
546        } else if (tokens.matches(STMT_DROP_OFFLINE_INDEX)) {
547            return parseStatement(tokens, STMT_DROP_OFFLINE_INDEX, parentNode, TYPE_DROP_INDEX_STATEMENT);
548        } else if (tokens.matches(STMT_DROP_ONLINE_INDEX)) {
549            return parseStatement(tokens, STMT_DROP_ONLINE_INDEX, parentNode, TYPE_DROP_INDEX_STATEMENT);
550        } else if (tokens.matches(STMT_DROP_LOGFILE_GROUP)) {
551            return parseStatement(tokens, STMT_DROP_LOGFILE_GROUP, parentNode, TYPE_DROP_LOGFILE_GROUP_STATEMENT);
552        } else if (tokens.matches(STMT_DROP_PROCEDURE)) {
553            return parseStatement(tokens, STMT_DROP_PROCEDURE, parentNode, TYPE_DROP_PROCEDURE_STATEMENT);
554        } else if (tokens.matches(STMT_DROP_SERVER)) {
555            return parseStatement(tokens, STMT_DROP_SERVER, parentNode, TYPE_DROP_SERVER_STATEMENT);
556        } else if (tokens.matches(STMT_DROP_TABLESPACE)) {
557            return parseStatement(tokens, STMT_DROP_TABLESPACE, parentNode, TYPE_DROP_TABLESPACE_STATEMENT);
558        } else if (tokens.matches(STMT_DROP_TRIGGER)) {
559            return parseStatement(tokens, STMT_DROP_TRIGGER, parentNode, TYPE_DROP_TRIGGER_STATEMENT);
560        }
561
562        return super.parseDropStatement(tokens, parentNode);
563    }
564
565    /**
566     * {@inheritDoc}
567     * 
568     * @see org.modeshape.sequencer.ddl.StandardDdlParser#getDataTypeStartWords()
569     */
570    @Override
571    protected List<String> getCustomDataTypeStartWords() {
572        return MySqlDataTypes.CUSTOM_DATATYPE_START_WORDS;
573    }
574
575    // ===========================================================================================================================
576    // ===========================================================================================================================
577    class MySqlDataTypeParser extends DataTypeParser implements MySqlDdlConstants.MySqlDataTypes {
578
579        // NOTE THAT MYSQL allows "UNSIGNED" and "ZEROFILL" as options AFTER the datatype definition
580        // Need to override and do a CHECK for and CONSUME them.
581
582        /**
583         * {@inheritDoc}
584         * 
585         * @see org.modeshape.sequencer.ddl.datatype.DataTypeParser#isCustomDataType(org.modeshape.sequencer.ddl.DdlTokenStream)
586         */
587        @Override
588        protected boolean isCustomDataType( DdlTokenStream tokens ) throws ParsingException {
589            // Loop through the registered statement start string arrays and look for exact matches.
590
591            for (String[] stmts : mysqlDataTypeStrings) {
592                if (tokens.matches(stmts)) return true;
593            }
594            return super.isCustomDataType(tokens);
595        }
596
597        @Override
598        protected DataType parseApproxNumericType( DdlTokenStream tokens ) throws ParsingException {
599            DataType dType = super.parseApproxNumericType(tokens);
600            tokens.canConsume("UNSIGNED");
601            tokens.canConsume("ZEROFILL");
602            tokens.canConsume("UNSIGNED");
603            return dType;
604        }
605
606        @Override
607        protected DataType parseBitStringType( DdlTokenStream tokens ) throws ParsingException {
608            return super.parseBitStringType(tokens);
609        }
610
611        @Override
612        protected DataType parseCharStringType( DdlTokenStream tokens ) throws ParsingException {
613            DataType result = super.parseCharStringType(tokens);
614
615            tokens.canConsume("FOR", "BIT", "DATA");
616
617            return result;
618        }
619
620        @Override
621        protected DataType parseCustomType( DdlTokenStream tokens ) throws ParsingException {
622            DataType dataType = null;
623
624            if (tokens.matches(DTYPE_FIXED) || tokens.matches(DTYPE_DOUBLE)) {
625                dataType = new DataType();
626                String typeName = tokens.consume();
627                dataType.setName(typeName);
628
629                int precision = 0;
630                int scale = 0;
631
632                if (tokens.matches(L_PAREN)) {
633                    consume(tokens, dataType, false, L_PAREN);
634                    precision = (int)parseLong(tokens, dataType);
635                    if (tokens.canConsume(COMMA)) {
636                        scale = (int)parseLong(tokens, dataType);
637                    } else {
638                        scale = getDefaultScale();
639                    }
640                    tokens.consume(R_PAREN);
641                } else {
642                    precision = getDefaultPrecision();
643                    scale = getDefaultScale();
644                }
645                dataType.setPrecision(precision);
646                dataType.setScale(scale);
647            } else if (tokens.matches(DTYPE_MEDIUMBLOB) || tokens.matches(DTYPE_LONGBLOB) || tokens.matches(DTYPE_BLOB)
648                       || tokens.matches(DTYPE_TINYBLOB) || tokens.matches(DTYPE_YEAR) || tokens.matches(DTYPE_DATETIME)
649                       || tokens.matches(DTYPE_BOOLEAN) || tokens.matches(DTYPE_BOOL)) {
650                String typeName = tokens.consume();
651                dataType = new DataType(typeName);
652            } else if (tokens.matches(DTYPE_MEDIUMINT) || tokens.matches(DTYPE_TINYINT) || tokens.matches(DTYPE_VARBINARY)
653                       || tokens.matches(DTYPE_BINARY) || tokens.matches(DTYPE_BIGINT)) {
654                String typeName = tokens.consume();
655                dataType = new DataType(typeName);
656                long length = getDefaultLength();
657                if (tokens.matches(L_PAREN)) {
658                    length = parseBracketedLong(tokens, dataType);
659                }
660                dataType.setLength(length);
661            } else if (tokens.matches(DTYPE_NATIONAL_VARCHAR)) {
662                String typeName = getStatementTypeName(DTYPE_NATIONAL_VARCHAR);
663                dataType = new DataType(typeName);
664                tokens.consume(DTYPE_NATIONAL_VARCHAR);
665                long length = getDefaultLength();
666                if (tokens.matches(L_PAREN)) {
667                    length = parseBracketedLong(tokens, dataType);
668                }
669                dataType.setLength(length);
670            } else if (tokens.matches(DTYPE_MEDIUMTEXT) || tokens.matches(DTYPE_TEXT) || tokens.matches(DTYPE_LONGTEXT)
671                       || tokens.matches(DTYPE_TINYTEXT)) {
672                String typeName = tokens.consume();
673                dataType = new DataType(typeName);
674                tokens.canConsume("BINARY");
675                tokens.canConsume("COLLATE", TokenStream.ANY_VALUE);
676                tokens.canConsume("CHARACTER", "SET", TokenStream.ANY_VALUE);
677                tokens.canConsume("COLLATE", TokenStream.ANY_VALUE);
678            } else if (tokens.matches(DTYPE_SET)) {
679                // SET(value1,value2,value3,...) [CHARACTER SET charset_name] [COLLATE collation_name]
680                String typeName = tokens.consume();
681                dataType = new DataType(typeName);
682
683                tokens.consume(L_PAREN);
684                do {
685                    tokens.consume();
686                } while (tokens.canConsume(COMMA));
687                tokens.consume(R_PAREN);
688
689                tokens.canConsume("COLLATE", TokenStream.ANY_VALUE);
690                tokens.canConsume("CHARACTER", "SET", TokenStream.ANY_VALUE);
691                tokens.canConsume("COLLATE", TokenStream.ANY_VALUE);
692            } else if (tokens.matches(DTYPE_ENUM)) {
693                // ENUM(value1,value2,value3,...) [CHARACTER SET charset_name] [COLLATE collation_name]
694                String typeName = tokens.consume();
695                dataType = new DataType(typeName);
696
697                tokens.consume(L_PAREN);
698                do {
699                    tokens.consume();
700                } while (tokens.canConsume(COMMA));
701                tokens.consume(R_PAREN);
702
703                tokens.canConsume("COLLATE", TokenStream.ANY_VALUE);
704                tokens.canConsume("CHARACTER", "SET", TokenStream.ANY_VALUE);
705                tokens.canConsume("COLLATE", TokenStream.ANY_VALUE);
706            }
707
708            if (dataType == null) {
709                dataType = super.parseCustomType(tokens);
710            }
711
712            // LOOKING for possible [UNSIGNED] [ZEROFILL] options
713
714            tokens.canConsume("UNSIGNED");
715            tokens.canConsume("ZEROFILL");
716            tokens.canConsume("UNSIGNED");
717
718            return dataType;
719        }
720
721        @Override
722        protected DataType parseDateTimeType( DdlTokenStream tokens ) throws ParsingException {
723            return super.parseDateTimeType(tokens);
724        }
725
726        @Override
727        protected DataType parseExactNumericType( DdlTokenStream tokens ) throws ParsingException {
728            DataType dType = super.parseExactNumericType(tokens);
729            tokens.canConsume("UNSIGNED");
730            tokens.canConsume("ZEROFILL");
731            tokens.canConsume("UNSIGNED");
732            return dType;
733        }
734
735    }
736
737}