1 /*
2  * Copyright 2015-2018 HuntLabs.cn
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 module hunt.sql.dialect.mysql.parser.MySqlStatementParser;
17 
18 import hunt.sql.SQLUtils;
19 import hunt.sql.ast;
20 import hunt.sql.ast.SQLParameter;
21 // import hunt.sql.ast.SQLParameter.ParameterType;
22 import hunt.sql.ast.expr;
23 import hunt.sql.ast.statement;
24 import hunt.sql.dialect.mysql.ast.MysqlForeignKey;
25 import hunt.sql.dialect.mysql.ast.clause.ConditionValue;
26 // import hunt.sql.dialect.mysql.ast.clause.ConditionValue.ConditionType;
27 import hunt.sql.dialect.mysql.ast.clause.MySqlCaseStatement;
28 // import hunt.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement;
29 import hunt.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatement;
30 import hunt.sql.dialect.mysql.ast.clause.MySqlDeclareConditionStatement;
31 import hunt.sql.dialect.mysql.ast.clause.MySqlDeclareHandlerStatement;
32 import hunt.sql.dialect.mysql.ast.clause.MySqlDeclareStatement;
33 import hunt.sql.dialect.mysql.ast.clause.MySqlHandlerType;
34 import hunt.sql.dialect.mysql.ast.clause.MySqlIterateStatement;
35 import hunt.sql.dialect.mysql.ast.clause.MySqlLeaveStatement;
36 import hunt.sql.dialect.mysql.ast.clause.MySqlRepeatStatement;
37 import hunt.sql.dialect.mysql.ast.clause.MySqlSelectIntoStatement;
38 import hunt.sql.dialect.mysql.ast.statement;
39 // import hunt.sql.dialect.mysql.ast.statement.MySqlLockTableStatement.LockType;
40 import hunt.sql.parser;
41 
42 import hunt.sql.visitor.SQLASTOutputVisitor;
43 import hunt.sql.util.FnvHash;
44 import hunt.sql.util.DBType;
45 import hunt.sql.dialect.mysql.parser.MySqlSelectParser;
46 import hunt.sql.dialect.mysql.parser.MySqlExprParser;
47 import hunt.sql.dialect.mysql.parser.MySqlCreateTableParser;
48 import std.algorithm.searching;
49 import hunt.String;
50 import hunt.collection;
51 import std.uni;
52 import hunt.Boolean;
53 
54 import hunt.text;
55 import hunt.sql.dialect.mysql.parser.MySqlSelectIntoParser;
56 
57 public class MySqlStatementParser : SQLStatementParser {
58 
59     private enum string AUTO_INCREMENT = "AUTO_INCREMENT";
60     private enum string COLLATE2 = "COLLATE";
61     private enum string CHAIN = "CHAIN";
62     private enum string ENGINES = "ENGINES";
63     private enum string ENGINE = "ENGINE";
64     private enum string BINLOG = "BINLOG";
65     private enum string EVENTS = "EVENTS";
66     private enum string SESSION = "SESSION";
67     private enum string GLOBAL = "GLOBAL";
68     private enum string VARIABLES = "VARIABLES";
69     private enum string STATUS = "STATUS";
70     private enum string RESET = "RESET";
71     private enum string DESCRIBE = "DESCRIBE";
72     private enum string WRITE = "WRITE";
73     private enum string READ = "READ";
74     private enum string LOCAL = "LOCAL";
75     private enum string TABLES = "TABLES";
76     private enum string TEMPORARY = "TEMPORARY";
77     private enum string SPATIAL = "SPATIAL";
78     private enum string LOW_PRIORITY = "LOW_PRIORITY";
79     private enum string CONNECTION = "CONNECTION";
80     private enum string EXTENDED = "EXTENDED";
81     private enum string PARTITIONS = "PARTITIONS";
82     private enum string FORMAT = "FORMAT";
83 
84     private int maxIntoClause = -1;
85 
86     public this(string sql) {
87         import std.stdio;
88         super(new MySqlExprParser(sql));
89     }
90 
91     public this(string sql, SQLParserFeature[] features...) {
92         super(new MySqlExprParser(sql, features));
93     }
94 
95     public this(string sql, bool keepComments) {
96         super(new MySqlExprParser(sql, keepComments));
97     }
98 
99     public this(string sql, bool skipComment, bool keepComments) {
100         super(new MySqlExprParser(sql, skipComment, keepComments));
101     }
102 
103     public this(Lexer lexer) {
104         super(new MySqlExprParser(lexer));
105     }
106 
107     public int getMaxIntoClause() {
108         return maxIntoClause;
109     }
110 
111     public void setMaxIntoClause(int maxIntoClause) {
112         this.maxIntoClause = maxIntoClause;
113     }
114 
115     override public SQLCreateTableStatement parseCreateTable() {
116         MySqlCreateTableParser parser = new MySqlCreateTableParser(this.exprParser);
117         return parser.parseCreateTable();
118     }
119 
120     override public SQLStatement parseSelect() {
121         MySqlSelectParser selectParser = createSQLSelectParser();
122 
123         SQLSelect select = selectParser.select();
124 
125         if (selectParser.returningFlag) {
126             return selectParser.updateStmt;
127         }
128 
129         return new SQLSelectStatement(select, DBType.MYSQL.name);
130     }
131 
132     override public SQLUpdateStatement parseUpdateStatement() {
133         return new MySqlSelectParser(this.exprParser, selectListCache).parseUpdateStatment();
134     }
135 
136     override protected MySqlUpdateStatement createUpdateStatement() {
137         return new MySqlUpdateStatement();
138     }
139 
140     override public MySqlDeleteStatement parseDeleteStatement() {
141         MySqlDeleteStatement deleteStatement = new MySqlDeleteStatement();
142 
143         if (lexer.token() == Token.DELETE) {
144             lexer.nextToken();
145 
146             if (lexer.token() == Token.COMMENT) {
147                 lexer.nextToken();
148             }
149 
150             if (lexer.token() == Token.HINT) {
151                 this.getExprParser().parseHints!(SQLCommentHint)((deleteStatement.getHints()));
152             }
153 
154             if (lexer.identifierEquals(FnvHash.Constants.LOW_PRIORITY)) {
155                 deleteStatement.setLowPriority(true);
156                 lexer.nextToken();
157             }
158 
159             if (lexer.identifierEquals("QUICK")) {
160                 deleteStatement.setQuick(true);
161                 lexer.nextToken();
162             }
163 
164             if (lexer.identifierEquals(FnvHash.Constants.IGNORE)) {
165                 deleteStatement.setIgnore(true);
166                 lexer.nextToken();
167             }
168 
169             if (lexer.identifierEquals(FnvHash.Constants.FORCE)) {
170                 Lexer.SavePoint savePoint = lexer.mark();
171                 lexer.nextToken();
172 
173                 if (lexer.token() == Token.ALL) {
174                     lexer.nextToken();
175                     acceptIdentifier("PARTITIONS");
176                     deleteStatement.setForceAllPartitions(true);
177                 } else if (lexer.identifierEquals(FnvHash.Constants.PARTITIONS)){
178                     lexer.nextToken();
179                     deleteStatement.setForceAllPartitions(true);
180                 } else if (lexer.token() == Token.PARTITION) {
181                     lexer.nextToken();
182                     SQLName partition = this.exprParser.name();
183                     deleteStatement.setForcePartition(partition);
184                 } else {
185                     lexer.reset(savePoint);
186                 }
187             }
188 
189             if (lexer.token() == Token.IDENTIFIER) {
190                 deleteStatement.setTableSource(createSQLSelectParser().parseTableSource());
191 
192                 if (lexer.token() == Token.FROM) {
193                     lexer.nextToken();
194                     SQLTableSource tableSource = createSQLSelectParser().parseTableSource();
195                     deleteStatement.setFrom(tableSource);
196                 }
197             } else if (lexer.token() == Token.FROM) {
198                 lexer.nextToken();
199                 deleteStatement.setTableSource(createSQLSelectParser().parseTableSource());
200             } else {
201                 throw new ParserException("syntax error. " ~ lexer.info());
202             }
203 
204             if (lexer.identifierEquals(FnvHash.Constants.USING)) {
205                 lexer.nextToken();
206 
207                 SQLTableSource tableSource = createSQLSelectParser().parseTableSource();
208                 deleteStatement.setUsing(tableSource);
209             }
210         }
211 
212         if (lexer.token() == (Token.WHERE)) {
213             lexer.nextToken();
214             SQLExpr where = this.exprParser.expr();
215             deleteStatement.setWhere(where);
216         }
217 
218         if (lexer.token() == (Token.ORDER)) {
219             SQLOrderBy orderBy = exprParser.parseOrderBy();
220             deleteStatement.setOrderBy(orderBy);
221         }
222 
223         deleteStatement.setLimit(this.exprParser.parseLimit());
224 
225         return deleteStatement;
226     }
227 
228     override public SQLStatement parseCreate() {
229         char markChar = lexer.current();
230         int markBp = lexer.bp();
231 
232         List!(string) comments = null;
233         if (lexer.isKeepComments() && lexer.hasComment()) {
234             comments = lexer.readAndResetComments();
235         }
236 
237         accept(Token.CREATE);
238 
239         bool replace = false;
240         if (lexer.token() == Token.OR) {
241             lexer.nextToken();
242             accept(Token.REPLACE);
243             replace = true;
244         }
245 
246         List!(SQLCommentHint) hints = this.exprParser.parseHints();
247 
248         if (lexer.token() == Token.TABLE || lexer.identifierEquals(TEMPORARY)) {
249             if (replace) {
250                 lexer.reset(markBp, markChar, Token.CREATE);
251             }
252             MySqlCreateTableParser parser = new MySqlCreateTableParser(this.exprParser);
253             MySqlCreateTableStatement stmt = parser.parseCreateTable(false);
254             stmt.setHints(hints);
255 
256             if (comments !is null) {
257                 stmt.addBeforeComment(comments);
258             }
259 
260             return stmt;
261         }
262 
263         if (lexer.token() == Token.DATABASE
264                 || lexer.token() == Token.SCHEMA) {
265             if (replace) {
266                 lexer.reset(markBp, markChar, Token.CREATE);
267             }
268             return parseCreateDatabase();
269         }
270 
271         if (lexer.token() == Token.UNIQUE || lexer.token() == Token.INDEX || lexer.token() == Token.FULLTEXT
272                 || lexer.identifierEquals(SPATIAL)) {
273             if (replace) {
274                 lexer.reset(markBp, markChar, Token.CREATE);
275             }
276             return parseCreateIndex(false);
277         }
278 
279         if (lexer.token() == Token.USER) {
280             if (replace) {
281                 lexer.reset(markBp, markChar, Token.CREATE);
282             }
283             return parseCreateUser();
284         }
285 
286         if (lexer.token() == Token.VIEW
287                 || lexer.identifierEquals(FnvHash.Constants.ALGORITHM)) {
288             if (replace) {
289                 lexer.reset(markBp, markChar, Token.CREATE);
290             }
291 
292             return parseCreateView();
293         }
294 
295         if (lexer.token() == Token.TRIGGER) {
296             lexer.reset(markBp, markChar, Token.CREATE);
297             return parseCreateTrigger();
298         }
299 
300         // parse create procedure
301         if (lexer.token() == Token.PROCEDURE ) {
302             if (replace) {
303                 lexer.reset(markBp, markChar, Token.CREATE);
304             }
305             return parseCreateProcedure();
306         }
307 
308         if (lexer.identifierEquals(FnvHash.Constants.DEFINER)) {
309 //            Lexer.SavePoint savePoint = lexer.mark();
310             lexer.nextToken();
311             accept(Token.EQ);
312             this.getExprParser().userName();
313 
314             if (lexer.identifierEquals(FnvHash.Constants.SQL)) {
315                 lexer.nextToken();
316                 acceptIdentifier("SECURITY");
317                 if (lexer.token() == Token.EQ) {
318                     lexer.nextToken();
319                 }
320                 lexer.nextToken();
321             }
322             if (lexer.identifierEquals(FnvHash.Constants.EVENT)) {
323                 lexer.reset(markBp, markChar, Token.CREATE);
324                 return parseCreateEvent();
325             } else if (lexer.token() == Token.TRIGGER) {
326                 lexer.reset(markBp, markChar, Token.CREATE);
327                 return parseCreateTrigger();
328             } else if (lexer.token() == Token.VIEW) {
329                 lexer.reset(markBp, markChar, Token.CREATE);
330                 return parseCreateView();
331             } else if (lexer.token() == Token.FUNCTION) {
332                 lexer.reset(markBp, markChar, Token.CREATE);
333                 return parseCreateFunction();
334             } else {
335                 lexer.reset(markBp, markChar, Token.CREATE);
336                 return parseCreateProcedure();
337             }
338         }
339 
340         if (lexer.token() == Token.FUNCTION) {
341             if (replace) {
342                 lexer.reset(markBp, markChar, Token.CREATE);
343             }
344             return parseCreateFunction();
345         }
346 
347         if (lexer.identifierEquals(FnvHash.Constants.LOGFILE)) {
348             return parseCreateLogFileGroup();
349         }
350 
351         if (lexer.identifierEquals(FnvHash.Constants.SERVER)) {
352             return parseCreateServer();
353         }
354 
355         if (lexer.token() == Token.TABLESPACE) {
356             return parseCreateTableSpace();
357         }
358 
359         throw new ParserException("TODO " ~ lexer.info());
360     }
361 
362     public SQLStatement parseCreateTableSpace() {
363         if (lexer.token() == Token.CREATE) {
364             accept(Token.CREATE);
365         }
366 
367         MySqlCreateTableSpaceStatement stmt = new MySqlCreateTableSpaceStatement();
368 
369         accept(Token.TABLESPACE);
370         stmt.setName(this.exprParser.name());
371 
372         if (lexer.identifierEquals(FnvHash.Constants.ADD)) {
373             lexer.nextToken();
374             acceptIdentifier("DATAFILE");
375             SQLExpr file = this.exprParser.primary();
376             stmt.setAddDataFile(file);
377         }
378 
379         for (;;) {
380             if (lexer.identifierEquals(FnvHash.Constants.INITIAL_SIZE)) {
381                 lexer.nextToken();
382                 if (lexer.token() == Token.EQ) {
383                     lexer.nextToken();
384                 }
385                 SQLExpr initialSize = this.exprParser.expr();
386                 stmt.setInitialSize(initialSize);
387             } else if (lexer.identifierEquals(FnvHash.Constants.FILE_BLOCK_SIZE)) {
388                 lexer.nextToken();
389                 if (lexer.token() == Token.EQ) {
390                     lexer.nextToken();
391                 }
392                 SQLExpr fileBlockSize = this.exprParser.expr();
393                 stmt.setFileBlockSize(fileBlockSize);
394             } else if (lexer.identifierEquals(FnvHash.Constants.EXTENT_SIZE)) {
395                 lexer.nextToken();
396                 if (lexer.token() == Token.EQ) {
397                     lexer.nextToken();
398                 }
399                 SQLExpr extentSize = this.exprParser.expr();
400                 stmt.setExtentSize(extentSize);
401             } else if (lexer.identifierEquals(FnvHash.Constants.AUTOEXTEND_SIZE)) {
402                 lexer.nextToken();
403                 if (lexer.token() == Token.EQ) {
404                     lexer.nextToken();
405                 }
406                 SQLExpr extentSize = this.exprParser.expr();
407                 stmt.setAutoExtentSize(extentSize);
408             } else if (lexer.identifierEquals(FnvHash.Constants.MAX_SIZE)) {
409                 lexer.nextToken();
410                 if (lexer.token() == Token.EQ) {
411                     lexer.nextToken();
412                 }
413                 SQLExpr size = this.exprParser.expr();
414                 stmt.setMaxSize(size);
415             } else if (lexer.identifierEquals(FnvHash.Constants.NODEGROUP)) {
416                 lexer.nextToken();
417                 if (lexer.token() == Token.EQ) {
418                     lexer.nextToken();
419                 }
420                 SQLExpr size = this.exprParser.expr();
421                 stmt.setNodeGroup(size);
422             } else if (lexer.identifierEquals(FnvHash.Constants.WAIT)) {
423                 lexer.nextToken();
424                 stmt.setWait(true);
425             } else if (lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
426                 lexer.nextToken();
427                 if (lexer.token() == Token.EQ) {
428                     lexer.nextToken();
429                 }
430                 SQLExpr engine = this.exprParser.expr();
431                 stmt.setEngine(engine);
432             } else if (lexer.token() == Token.COMMENT) {
433                 lexer.nextToken();
434                 SQLExpr comment = this.exprParser.expr();
435                 stmt.setComment(comment);
436             } else if (lexer.token() == Token.USE) {
437                 lexer.nextToken();
438                 acceptIdentifier("LOGFILE");
439                 accept(Token.GROUP);
440 
441                 SQLExpr logFileGroup = this.exprParser.expr();
442                 stmt.setFileBlockSize(logFileGroup);
443             } else {
444                 break;
445             }
446         }
447         return stmt;
448     }
449 
450     public SQLStatement parseCreateServer() {
451         if (lexer.token() == Token.CREATE) {
452             accept(Token.CREATE);
453         }
454 
455         MySqlCreateServerStatement stmt = new MySqlCreateServerStatement();
456 
457         acceptIdentifier("SERVER");
458         stmt.setName(this.exprParser.name());
459 
460         accept(Token.FOREIGN);
461         acceptIdentifier("DATA");
462         acceptIdentifier("WRAPPER");
463         stmt.setForeignDataWrapper(this.exprParser.name());
464 
465         acceptIdentifier("OPTIONS");
466         accept(Token.LPAREN);
467         for (;;) {
468             if (lexer.identifierEquals(FnvHash.Constants.HOST)) {
469                 lexer.nextToken();
470                 SQLExpr host = this.exprParser.expr();
471                 stmt.setHost(host);
472             } else if (lexer.token() == Token.USER) {
473                 lexer.nextToken();
474                 SQLExpr user = this.exprParser.expr();
475                 stmt.setUser(user);
476             } else if (lexer.token() == Token.DATABASE) {
477                 lexer.nextToken();
478                 SQLExpr db = this.exprParser.expr();
479                 stmt.setDatabase(db);
480             } else if (lexer.identifierEquals(FnvHash.Constants.PASSWORD)) {
481                 lexer.nextToken();
482                 SQLExpr pwd = this.exprParser.expr();
483                 stmt.setPassword(pwd);
484             } else if (lexer.identifierEquals(FnvHash.Constants.SOCKET)) {
485                 lexer.nextToken();
486                 SQLExpr sock = this.exprParser.expr();
487                 stmt.setSocket(sock);
488             } else if (lexer.identifierEquals(FnvHash.Constants.OWNER)) {
489                 lexer.nextToken();
490                 SQLExpr owner = this.exprParser.expr();
491                 stmt.setOwner(owner);
492             } else if (lexer.identifierEquals(FnvHash.Constants.PORT)) {
493                 lexer.nextToken();
494                 SQLExpr port = this.exprParser.expr();
495                 stmt.setPort(port);
496             }
497 
498             if (lexer.token() == Token.COMMA) {
499                 lexer.nextToken();
500             } else {
501                 break;
502             }
503         }
504         accept(Token.RPAREN);
505         return stmt;
506     }
507 
508     override public SQLStatement parseCreateIndex(bool acceptCreate) {
509         if (acceptCreate) {
510             accept(Token.CREATE);
511         }
512 
513         SQLCreateIndexStatement stmt = new SQLCreateIndexStatement();
514 
515         if (lexer.token() == Token.UNIQUE) {
516             stmt.setType("UNIQUE");
517             lexer.nextToken();
518         } else if (lexer.token() == Token.FULLTEXT) {
519             stmt.setType("FULLTEXT");
520             lexer.nextToken();
521         } else if (lexer.identifierEquals(SPATIAL)) {
522             stmt.setType(SPATIAL);
523             lexer.nextToken();
524         }
525 
526         accept(Token.INDEX);
527 
528         stmt.setName(this.exprParser.name());
529 
530         parseCreateIndexUsing(stmt);
531 
532         accept(Token.ON);
533 
534         stmt.setTable(this.exprParser.name());
535 
536         accept(Token.LPAREN);
537 
538         for (; ; ) {
539             SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
540             stmt.addItem(item);
541             if (lexer.token() == Token.COMMA) {
542                 lexer.nextToken();
543                 continue;
544             }
545             break;
546         }
547         accept(Token.RPAREN);
548 
549         for (;;) {
550             if (lexer.identifierEquals(FnvHash.Constants.USING)) {
551                 parseCreateIndexUsing(stmt);
552             } else if (lexer.token() == Token.COMMENT) {
553                 lexer.nextToken();
554                 SQLExpr comment = this.exprParser.expr();
555                 stmt.setComment(comment);
556             } else {
557                 break;
558             }
559         }
560 
561         return stmt;
562     }
563 
564     private void parseCreateIndexUsing(SQLCreateIndexStatement stmt) {
565         if (lexer.identifierEquals(FnvHash.Constants.USING)) {
566             lexer.nextToken();
567 
568             if (lexer.identifierEquals(FnvHash.Constants.BTREE)) {
569                 stmt.setUsing("BTREE");
570                 lexer.nextToken();
571             } else if (lexer.identifierEquals(FnvHash.Constants.HASH)) {
572                 stmt.setUsing("HASH");
573                 lexer.nextToken();
574             } else {
575                 throw new ParserException("TODO " ~ lexer.info());
576             }
577         }
578     }
579 
580     override public SQLStatement parseCreateUser() {
581         if (lexer.token() == Token.CREATE) {
582             lexer.nextToken();
583         }
584 
585         accept(Token.USER);
586 
587         MySqlCreateUserStatement stmt = new MySqlCreateUserStatement();
588 
589         for (; ; ) {
590             MySqlCreateUserStatement.UserSpecification userSpec = new MySqlCreateUserStatement.UserSpecification();
591 
592             SQLExpr expr = exprParser.primary();
593             userSpec.setUser(expr);
594 
595             if (lexer.token() == Token.IDENTIFIED) {
596                 lexer.nextToken();
597                 if (lexer.token() == Token.BY) {
598                     lexer.nextToken();
599 
600                     if (lexer.identifierEquals("PASSWORD")) {
601                         lexer.nextToken();
602                         userSpec.setPasswordHash(true);
603                     }
604 
605                     SQLCharExpr password = cast(SQLCharExpr) this.exprParser.expr();
606                     userSpec.setPassword(password);
607                 } else if (lexer.token() == Token.WITH) {
608                     lexer.nextToken();
609                     userSpec.setAuthPlugin(this.exprParser.expr());
610                 }
611             }
612 
613             stmt.addUser(userSpec);
614 
615             if (lexer.token() == Token.COMMA) {
616                 lexer.nextToken();
617                 continue;
618             }
619 
620             break;
621         }
622 
623         return stmt;
624     }
625 
626     override public SQLStatement parseKill() {
627         accept(Token.KILL);
628 
629         MySqlKillStatement stmt = new MySqlKillStatement();
630 
631         if (lexer.identifierEquals("CONNECTION")) {
632             stmt.setType(MySqlKillStatement.Type.CONNECTION);
633             lexer.nextToken();
634         } else if (lexer.identifierEquals("QUERY")) {
635             stmt.setType(MySqlKillStatement.Type.QUERY);
636             lexer.nextToken();
637         } else if (lexer.token() == Token.LITERAL_INT) {
638             // skip
639         } else {
640             throw new ParserException("not support kill type " ~ lexer.token() ~ ". " ~ lexer.info());
641         }
642 
643         this.exprParser.exprList(stmt.getThreadIds(), stmt);
644         return stmt;
645     }
646 
647     public SQLStatement parseBinlog() {
648         acceptIdentifier("binlog");
649 
650         MySqlBinlogStatement stmt = new MySqlBinlogStatement();
651 
652         SQLExpr expr = this.exprParser.expr();
653         stmt.setExpr(expr);
654 
655         return stmt;
656     }
657 
658     public MySqlAnalyzeStatement parseAnalyze() {
659         accept(Token.ANALYZE);
660         accept(Token.TABLE);
661 
662         MySqlAnalyzeStatement stmt = new MySqlAnalyzeStatement();
663         List!(SQLName) names = new ArrayList!(SQLName)();
664         this.exprParser.names(names, stmt);
665 
666         foreach(SQLName name ; names) {
667             stmt.addTableSource(new SQLExprTableSource(name));
668         }
669         return stmt;
670     }
671 
672     public MySqlOptimizeStatement parseOptimize() {
673         accept(Token.OPTIMIZE);
674         accept(Token.TABLE);
675 
676         MySqlOptimizeStatement stmt = new MySqlOptimizeStatement();
677         List!(SQLName) names = new ArrayList!(SQLName)();
678         this.exprParser.names(names, stmt);
679 
680         foreach(SQLName name ; names) {
681             stmt.addTableSource(new SQLExprTableSource(name));
682         }
683         return stmt;
684     }
685 
686     public SQLStatement parseReset() {
687         acceptIdentifier(RESET);
688 
689         MySqlResetStatement stmt = new MySqlResetStatement();
690 
691         for (; ; ) {
692             if (lexer.token() == Token.IDENTIFIER) {
693                 if (lexer.identifierEquals("QUERY")) {
694                     lexer.nextToken();
695                     accept(Token.CACHE);
696                     stmt.getOptions().add("QUERY CACHE");
697                 } else {
698                     stmt.getOptions().add(lexer.stringVal());
699                     lexer.nextToken();
700                 }
701 
702                 if (lexer.token() == Token.COMMA) {
703                     lexer.nextToken();
704                     continue;
705                 }
706             }
707             break;
708         }
709 
710         return stmt;
711     }
712 
713     override public bool parseStatementListDialect(List!(SQLStatement) statementList) {
714         if (lexer.identifierEquals("PREPARE")) {
715             MySqlPrepareStatement stmt = parsePrepare();
716             statementList.add(stmt);
717             return true;
718         }
719 
720         if (lexer.identifierEquals("EXECUTE")) {
721             MySqlExecuteStatement stmt = parseExecute();
722             statementList.add(stmt);
723             return true;
724         }
725 
726         if (lexer.identifierEquals("DEALLOCATE")) {
727             MysqlDeallocatePrepareStatement stmt = parseDeallocatePrepare();
728             statementList.add(stmt);
729             return true;
730         }
731 
732         if (lexer.identifierEquals("LOAD")) {
733             SQLStatement stmt = parseLoad();
734             statementList.add(stmt);
735             return true;
736         }
737 
738         if (lexer.token() == Token.REPLACE) {
739             SQLReplaceStatement stmt = parseReplace();
740             statementList.add(stmt);
741             return true;
742         }
743 
744         if (lexer.identifierEquals("START")) {
745             SQLStartTransactionStatement stmt = parseStart();
746             statementList.add(stmt);
747             return true;
748         }
749 
750         if (lexer.token() == Token.SHOW) {
751             SQLStatement stmt = parseShow();
752             statementList.add(stmt);
753             return true;
754         }
755 
756         if (lexer.token() == Token.EXPLAIN) {
757             SQLStatement stmt = this.parseExplain();
758             statementList.add(stmt);
759             return true;
760         }
761 
762 
763         if (lexer.identifierEquals(BINLOG)) {
764             SQLStatement stmt = parseBinlog();
765             statementList.add(stmt);
766             return true;
767         }
768 
769         if (lexer.identifierEquals(RESET)) {
770             SQLStatement stmt = parseReset();
771             statementList.add(stmt);
772             return true;
773         }
774 
775         if (lexer.token() == Token.ANALYZE) {
776             SQLStatement stmt = parseAnalyze();
777             statementList.add(stmt);
778             return true;
779         }
780 
781         if (lexer.token() == Token.OPTIMIZE) {
782             SQLStatement stmt = parseOptimize();
783             statementList.add(stmt);
784             return true;
785         }
786 
787         if (lexer.identifierEquals("HELP")) {
788             lexer.nextToken();
789             MySqlHelpStatement stmt = new MySqlHelpStatement();
790             stmt.setContent(this.exprParser.primary());
791             statementList.add(stmt);
792             return true;
793         }
794 
795         if (lexer.identifierEquals("FLUSH")) {
796             SQLStatement stmt = parseFlush();
797             statementList.add(stmt);
798             return true;
799         }
800 
801         if (lexer.token() == Token.DESC || lexer.identifierEquals(DESCRIBE)) {
802             SQLStatement stmt = parseDescribe();
803             statementList.add(stmt);
804             return true;
805         }
806 
807         if (lexer.token() == Token.LOCK) {
808             lexer.nextToken();
809             string val = lexer.stringVal();
810             bool isLockTables = equalsIgnoreCase(TABLES, val) && lexer.token() == Token.IDENTIFIER;
811             bool isLockTable = equalsIgnoreCase("TABLE",val) && lexer.token() == Token.TABLE;
812             if (isLockTables || isLockTable) {
813                 lexer.nextToken();
814             } else {
815                 setErrorEndPos(lexer.pos());
816                 throw new ParserException("syntax error, expect TABLES or TABLE, actual " ~ lexer.token() ~ ", " ~ lexer.info());
817             }
818 
819             MySqlLockTableStatement stmt = new MySqlLockTableStatement();
820 
821             for(;;) {
822                 MySqlLockTableStatement.Item item = new MySqlLockTableStatement.Item();
823 
824                 SQLExprTableSource tableSource = null;
825                 SQLName tableName = this.exprParser.name();
826 
827 
828                 if (lexer.token() == Token.AS) {
829                     lexer.nextToken();
830                     string as = lexer.stringVal();
831                     tableSource = new SQLExprTableSource(tableName, as);
832                     lexer.nextToken();
833                 } else {
834                     tableSource = new SQLExprTableSource(tableName);
835                 }
836                 item.setTableSource(tableSource);
837                 stmt.getItems().add(item);
838 
839                 if (lexer.token() == Token.COMMA) {
840                     lexer.nextToken();
841                     continue;
842                 }
843 
844                 if (lexer.identifierEquals(READ)) {
845                     lexer.nextToken();
846                     if (lexer.identifierEquals(LOCAL)) {
847                         lexer.nextToken();
848                         item.setLockType(MySqlLockTableStatement.LockType.READ_LOCAL);
849                     } else {
850                         item.setLockType(MySqlLockTableStatement.LockType.READ);
851                     }
852                 } else if (lexer.identifierEquals(WRITE)) {
853                     lexer.nextToken();
854                     item.setLockType(MySqlLockTableStatement.LockType.WRITE);
855                 } else if (lexer.identifierEquals(FnvHash.Constants.LOW_PRIORITY)) {
856                     lexer.nextToken();
857                     acceptIdentifier(WRITE);
858                     lexer.nextToken();
859                     item.setLockType(MySqlLockTableStatement.LockType.LOW_PRIORITY_WRITE);
860                 } else {
861                     throw new ParserException(
862                             "syntax error, expect READ or WRITE OR AS, actual " ~ lexer.token() ~ ", " ~ lexer.info());
863                 }
864 
865                 if (lexer.token() == Token.HINT) {
866                     item.setHints(this.exprParser.parseHints());
867                 }
868 
869                 if (lexer.token() == Token.COMMA) {
870                     lexer.nextToken();
871                     continue;
872                 }
873                 break;
874             }
875 
876             statementList.add(stmt);
877             return true;
878         }
879 
880         if (lexer.identifierEquals("UNLOCK")) {
881             lexer.nextToken();
882             string val = lexer.stringVal();
883             bool isUnLockTables = equalsIgnoreCase(TABLES, val) && lexer.token() == Token.IDENTIFIER;
884             bool isUnLockTable = equalsIgnoreCase("TABLE",val) && lexer.token() == Token.TABLE;
885             statementList.add(new MySqlUnlockTablesStatement());
886             if (isUnLockTables || isUnLockTable) {
887                 lexer.nextToken();
888             } else {
889                 setErrorEndPos(lexer.pos());
890                 throw new ParserException("syntax error, expect TABLES or TABLE, actual " ~ lexer.token() ~ ", " ~ lexer.info());
891             }
892             return true;
893         }
894 
895         if (lexer.identifierEquals(FnvHash.Constants.CHECKSUM)) {
896             statementList.add(this.parseChecksum());
897             return true;
898         }
899 
900         if (lexer.token() == Token.HINT) {
901             List!(SQLCommentHint) hints = this.exprParser.parseHints();
902 
903             bool tddlHints = false;
904             bool accept = false;
905 
906 
907             bool acceptHint = false;
908             switch (lexer.token()) {
909                 case Token.SELECT:
910                 case Token.WITH:
911                 case Token.DELETE:
912                 case Token.UPDATE:
913                 case Token.INSERT:
914                 case Token.SHOW:
915                 case Token.REPLACE:
916                 case Token.TRUNCATE:
917                 case Token.DROP:
918                 case Token.ALTER:
919                 case Token.CREATE:
920                 case Token.CHECK:
921                 case Token.SET:
922                     acceptHint = true;
923                     break;
924                 case Token.IDENTIFIER:
925                     acceptHint = lexer.hash_lower() == FnvHash.Constants.DUMP
926                             || lexer.hash_lower() == FnvHash.Constants.RENAME;
927                     break;
928                 default:
929                     break;
930             }
931             if (hints.size() == 1
932                     && statementList.size() == 0
933                     && acceptHint) {
934                 SQLCommentHint hint = hints.get(0);
935                 string hintText = toUpper(hint.getText());
936                 if (startsWith(hintText,"+TDDL")
937                         || startsWith(hintText,"+ TDDL")
938                         || startsWith(hintText,"TDDL")
939                         || startsWith(hintText,"!TDDL"))
940                 {
941                     tddlHints = true;
942                 } else if(startsWith(hintText,"+")) {
943                     accept = true;
944                 }
945             }
946 
947             if (tddlHints) {
948                 SQLStatementImpl stmt = cast(SQLStatementImpl)this.parseStatement();
949                 stmt.setHeadHints(hints);
950                 statementList.add(stmt);
951                 return true;
952             } else if (accept) {
953                 SQLStatementImpl stmt = cast(SQLStatementImpl) this.parseStatement();
954                 stmt.setHeadHints(hints);
955                 statementList.add(stmt);
956                 return true;
957             }
958 
959             MySqlHintStatement stmt = new MySqlHintStatement();
960             stmt.setHints(hints);
961 
962             statementList.add(stmt);
963             return true;
964         }
965 
966         if (lexer.token() == Token.BEGIN) {
967             statementList.add(this.parseBlock());
968             return true;
969         }
970 
971         if (lexer.token() == Token.IDENTIFIER) {
972             string label = lexer.stringVal();
973             char ch = lexer.current();
974             int bp = lexer.bp();
975             lexer.nextToken();
976             if (lexer.token() == Token.VARIANT && lexer.stringVal() == ":") {
977                 lexer.nextToken();
978                 if (lexer.token() == Token.LOOP) {
979                     // parse loop statement
980                     statementList.add(this.parseLoop(label));
981                 } else if (lexer.token() == Token.WHILE) {
982                     // parse while statement with label
983                     statementList.add(this.parseWhile(label));
984                 } else if (lexer.token() == Token.BEGIN) {
985                     // parse begin-end statement with label
986                     SQLBlockStatement block = this.parseBlock(label);
987                     statementList.add(block);
988                 } else if (lexer.token() == Token.REPEAT) {
989                     // parse repeat statement with label
990                     statementList.add(this.parseRepeat(label));
991                 }
992                 return true;
993             } else {
994                 lexer.reset(bp, ch, Token.IDENTIFIER);
995             }
996 
997         }
998 
999         return false;
1000     }
1001 
1002     public SQLStatement parseFlush() {
1003         acceptIdentifier("FLUSH");
1004         MySqlFlushStatement stmt = new MySqlFlushStatement();
1005 
1006         if (lexer.identifierEquals("NO_WRITE_TO_BINLOG")) {
1007             lexer.nextToken();
1008             stmt.setNoWriteToBinlog(true);
1009         }
1010 
1011         if (lexer.identifierEquals("LOCAL")) {
1012             lexer.nextToken();
1013             stmt.setLocal(true);
1014         }
1015 
1016         for (;;) {
1017             if (lexer.token() == Token.BINARY || lexer.identifierEquals("BINARY")) {
1018                 lexer.nextToken();
1019                 acceptIdentifier("LOGS");
1020                 stmt.setBinaryLogs(true);
1021             } else if (lexer.identifierEquals("DES_KEY_FILE")) {
1022                 lexer.nextToken();
1023                 stmt.setDesKeyFile(true);
1024             } else if (lexer.identifierEquals("ENGINE")) {
1025                 lexer.nextToken();
1026                 acceptIdentifier("LOGS");
1027                 stmt.setEngineLogs(true);
1028             } else if (lexer.identifierEquals("ERROR")) {
1029                 lexer.nextToken();
1030                 acceptIdentifier("LOGS");
1031                 stmt.setErrorLogs(true);
1032             } else if (lexer.identifierEquals("GENERAL")) {
1033                 lexer.nextToken();
1034                 acceptIdentifier("LOGS");
1035                 stmt.setGeneralLogs(true);
1036             } else if (lexer.identifierEquals("HOSTS")) {
1037                 lexer.nextToken();
1038                 stmt.setHots(true);
1039             } else if (lexer.identifierEquals("LOGS")) {
1040                 lexer.nextToken();
1041                 stmt.setLogs(true);
1042             } else if (lexer.identifierEquals("PRIVILEGES")) {
1043                 lexer.nextToken();
1044                 stmt.setPrivileges(true);
1045             } else if (lexer.identifierEquals("OPTIMIZER_COSTS")) {
1046                 lexer.nextToken();
1047                 stmt.setOptimizerCosts(true);
1048             } else if (lexer.identifierEquals("QUERY")) {
1049                 lexer.nextToken();
1050                 accept(Token.CACHE);
1051                 stmt.setQueryCache(true);
1052             }  else if (lexer.identifierEquals("RELAY")) {
1053                 lexer.nextToken();
1054                 acceptIdentifier("LOGS");
1055                 stmt.setRelayLogs(true);
1056                 if (lexer.token() == Token.FOR) {
1057                     lexer.nextToken();
1058                     acceptIdentifier("CHANNEL");
1059                     stmt.setRelayLogsForChannel(this.exprParser.primary());
1060                 }
1061             } else if (lexer.identifierEquals("SLOW")) {
1062                 lexer.nextToken();
1063                 acceptIdentifier("LOGS");
1064                 stmt.setSlowLogs(true);
1065             } else if (lexer.identifierEquals(STATUS)) {
1066                 lexer.nextToken();
1067                 stmt.setStatus(true);
1068             } else  if (lexer.identifierEquals("USER_RESOURCES")) {
1069                 lexer.nextToken();
1070                 stmt.setUserResources(true);
1071             } else if (lexer.token() == Token.COMMA) {
1072                 lexer.nextToken();
1073                 continue;
1074             } else {
1075                 break;
1076             }
1077         }
1078 
1079         if (lexer.identifierEquals("TABLES")) {
1080             lexer.nextToken();
1081 
1082             stmt.setTableOption(true);
1083 
1084             if (lexer.token() == Token.WITH) {
1085                 lexer.nextToken();
1086                 acceptIdentifier("READ");
1087                 accept(Token.LOCK);
1088                 stmt.setWithReadLock(true);
1089             }
1090             for(;;) {
1091                 if (lexer.token() == Token.IDENTIFIER) {
1092                     for (; ; ) {
1093                         SQLName name = this.exprParser.name();
1094                         stmt.addTable(name);
1095 
1096                         if (lexer.token() == Token.COMMA) {
1097                             lexer.nextToken();
1098                             continue;
1099                         }
1100                         break;
1101                     }
1102                     break;
1103                 }
1104                 break;
1105             }
1106 
1107             if (stmt.getTables().size() != 0) {
1108                 if (lexer.token() == Token.FOR) {
1109                     lexer.nextToken();
1110                     acceptIdentifier("EXPORT");
1111                     stmt.setForExport(true);
1112                 } else if (lexer.token() == Token.WITH) {
1113                     lexer.nextToken();
1114                     acceptIdentifier("READ");
1115                     accept(Token.LOCK);
1116                     stmt.setWithReadLock(true);
1117                 }
1118             }
1119 
1120         }
1121 
1122         return stmt;
1123     }
1124 
1125     override public SQLBlockStatement parseBlock() {
1126         SQLBlockStatement block = new SQLBlockStatement();
1127         block.setDbType(dbType);
1128 
1129         accept(Token.BEGIN);
1130         List!(SQLStatement) statementList = block.getStatementList();
1131         this.parseStatementList(statementList, -1, block);
1132 
1133         if (lexer.token() != Token.END
1134                 && statementList.size() > 0
1135                 && (cast(SQLCommitStatement)statementList.get(statementList.size() - 1) !is null
1136                     || cast(SQLRollbackStatement)statementList.get(statementList.size() - 1) !is null)) {
1137             block.setEndOfCommit(true);
1138             return block;
1139         }
1140         accept(Token.END);
1141 
1142         return block;
1143     }
1144 
1145     override public MySqlExplainStatement parseDescribe() {
1146         // see https://dev.mysql.com/doc/refman/5.7/en/explain.html
1147         MySqlExplainStatement describe = new MySqlExplainStatement();
1148 
1149         // {DESCRIBE | DESC}
1150         if (lexer.token() == Token.DESC || lexer.identifierEquals(DESCRIBE)) {
1151             lexer.nextToken();
1152             describe.setDescribe(true);
1153         } else {
1154             throw new ParserException("expect one of {DESCRIBE | DESC} , actual " ~ lexer.token() ~ ", " ~ lexer.info());
1155         }
1156 
1157         return parseExplain(describe);
1158     }
1159 
1160     override public MySqlExplainStatement parseExplain() {
1161         // see https://dev.mysql.com/doc/refman/5.7/en/explain.html
1162         MySqlExplainStatement explain = new MySqlExplainStatement();
1163 
1164         // {EXPLAIN}
1165         if (lexer.token() == Token.EXPLAIN) {
1166             lexer.nextToken();
1167         } else {
1168             throw new ParserException("expect EXPLAIN , actual " ~ lexer.token() ~ ", " ~ lexer.info());
1169         }
1170 
1171         return parseExplain(explain);
1172     }
1173 
1174 
1175     private MySqlExplainStatement parseExplain(MySqlExplainStatement explain) {
1176 
1177         if (lexer.token() == Token.HINT) {
1178             List!(SQLCommentHint) hints = this.exprParser.parseHints();
1179             explain.setHints(hints);
1180         }
1181         // see https://dev.mysql.com/doc/refman/5.7/en/explain.html
1182 
1183         bool table = false;
1184         if (lexer.token() == Token.IDENTIFIER) {
1185              string stringVal = lexer.stringVal();
1186 
1187              if (equalsIgnoreCase(stringVal, EXTENDED)
1188                      || equalsIgnoreCase(stringVal, PARTITIONS)) {
1189                  explain.setType(stringVal);
1190                  lexer.nextToken();
1191              } else if (equalsIgnoreCase(stringVal, FORMAT)) {
1192                  explain.setType(stringVal);
1193                  lexer.nextToken();
1194                  accept(Token.EQ);
1195 
1196                  string format = lexer.stringVal();
1197                  explain.setFormat(format);
1198                  accept(Token.IDENTIFIER);
1199              } else {
1200                  explain.setTableName(exprParser.name());
1201                  if (lexer.token() == Token.IDENTIFIER) {
1202                      explain.setColumnName(exprParser.name());
1203                  } else if (lexer.token() == Token.LITERAL_CHARS) {
1204                      explain.setWild(exprParser.expr());
1205                  }
1206                  table = true;
1207              }
1208         }
1209 
1210         if (lexer.token() == Token.FOR) {
1211             lexer.nextToken();
1212             acceptIdentifier(CONNECTION);
1213             explain.setConnectionId(exprParser.expr());
1214         } else if (!table) {
1215             explain.setStatement(this.parseStatement());
1216         }
1217 
1218         return explain;
1219     }
1220 
1221     override public SQLStatement parseShow() {
1222         accept(Token.SHOW);
1223 
1224         if (lexer.token() == Token.COMMENT) {
1225             lexer.nextToken();
1226         }
1227 
1228         bool full = false;
1229         if (lexer.token() == Token.FULL) {
1230             lexer.nextToken();
1231             full = true;
1232         }
1233 
1234         if (lexer.identifierEquals("PROCESSLIST")) {
1235             lexer.nextToken();
1236             MySqlShowProcessListStatement stmt = new MySqlShowProcessListStatement();
1237             stmt.setFull(full);
1238             return stmt;
1239         }
1240 
1241         if (lexer.identifierEquals("COLUMNS") || lexer.identifierEquals("FIELDS")) {
1242             lexer.nextToken();
1243 
1244             MySqlShowColumnsStatement stmt = parseShowColumns();
1245             stmt.setFull(full);
1246 
1247             return stmt;
1248         }
1249 
1250         if (lexer.identifierEquals("COLUMNS")) {
1251             lexer.nextToken();
1252 
1253             MySqlShowColumnsStatement stmt = parseShowColumns();
1254 
1255             return stmt;
1256         }
1257 
1258         if (lexer.identifierEquals(TABLES)) {
1259             lexer.nextToken();
1260 
1261             SQLShowTablesStatement stmt = parseShowTabless();
1262             stmt.setFull(full);
1263 
1264             return stmt;
1265         }
1266 
1267         if (lexer.identifierEquals("DATABASES")) {
1268             lexer.nextToken();
1269 
1270             MySqlShowDatabasesStatement stmt = parseShowDatabases();
1271 
1272             return stmt;
1273         }
1274 
1275         if (lexer.identifierEquals("WARNINGS")) {
1276             lexer.nextToken();
1277 
1278             MySqlShowWarningsStatement stmt = parseShowWarnings();
1279 
1280             return stmt;
1281         }
1282 
1283         if (lexer.identifierEquals("COUNT")) {
1284             lexer.nextToken();
1285             accept(Token.LPAREN);
1286             accept(Token.STAR);
1287             accept(Token.RPAREN);
1288 
1289             if (lexer.identifierEquals(FnvHash.Constants.ERRORS)) {
1290                 lexer.nextToken();
1291 
1292                 MySqlShowErrorsStatement stmt = new MySqlShowErrorsStatement();
1293                 stmt.setCount(true);
1294 
1295                 return stmt;
1296             } else {
1297                 acceptIdentifier("WARNINGS");
1298 
1299                 MySqlShowWarningsStatement stmt = new MySqlShowWarningsStatement();
1300                 stmt.setCount(true);
1301 
1302                 return stmt;
1303             }
1304         }
1305 
1306         if (lexer.identifierEquals(FnvHash.Constants.ERRORS)) {
1307             lexer.nextToken();
1308 
1309             MySqlShowErrorsStatement stmt = new MySqlShowErrorsStatement();
1310             stmt.setLimit(this.exprParser.parseLimit());
1311 
1312             return stmt;
1313         }
1314 
1315         if (lexer.identifierEquals(STATUS)) {
1316             lexer.nextToken();
1317 
1318             MySqlShowStatusStatement stmt = parseShowStatus();
1319 
1320             return stmt;
1321         }
1322 
1323         if (lexer.identifierEquals(VARIABLES)) {
1324             lexer.nextToken();
1325 
1326             MySqlShowVariantsStatement stmt = parseShowVariants();
1327 
1328             return stmt;
1329         }
1330 
1331         if (lexer.identifierEquals(GLOBAL)) {
1332             lexer.nextToken();
1333 
1334             if (lexer.identifierEquals(STATUS)) {
1335                 lexer.nextToken();
1336                 MySqlShowStatusStatement stmt = parseShowStatus();
1337                 stmt.setGlobal(true);
1338                 return stmt;
1339             }
1340 
1341             if (lexer.identifierEquals(VARIABLES)) {
1342                 lexer.nextToken();
1343                 MySqlShowVariantsStatement stmt = parseShowVariants();
1344                 stmt.setGlobal(true);
1345                 return stmt;
1346             }
1347         }
1348 
1349         if (lexer.identifierEquals(SESSION)) {
1350             lexer.nextToken();
1351 
1352             if (lexer.identifierEquals(STATUS)) {
1353                 lexer.nextToken();
1354                 MySqlShowStatusStatement stmt = parseShowStatus();
1355                 stmt.setSession(true);
1356                 return stmt;
1357             }
1358 
1359             if (lexer.identifierEquals(VARIABLES)) {
1360                 lexer.nextToken();
1361                 MySqlShowVariantsStatement stmt = parseShowVariants();
1362                 stmt.setSession(true);
1363                 return stmt;
1364             }
1365         }
1366 
1367         if (lexer.identifierEquals("COBAR_STATUS")) {
1368             lexer.nextToken();
1369             return new CobarShowStatus();
1370         }
1371 
1372         if (lexer.identifierEquals("AUTHORS")) {
1373             lexer.nextToken();
1374             return new MySqlShowAuthorsStatement();
1375         }
1376 
1377         if (lexer.token() == Token.BINARY) {
1378             lexer.nextToken();
1379             acceptIdentifier("LOGS");
1380             return new MySqlShowBinaryLogsStatement();
1381         }
1382 
1383         if (lexer.identifierEquals("MASTER")) {
1384             lexer.nextToken();
1385             if (lexer.identifierEquals("LOGS")) {
1386                 lexer.nextToken();
1387                 return new MySqlShowMasterLogsStatement();
1388             }
1389             acceptIdentifier(STATUS);
1390             return new MySqlShowMasterStatusStatement();
1391         }
1392 
1393         if (lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
1394             lexer.nextToken();
1395             accept(Token.SET);
1396             MySqlShowCharacterSetStatement stmt = new MySqlShowCharacterSetStatement();
1397 
1398             if (lexer.token() == Token.LIKE) {
1399                 lexer.nextToken();
1400                 stmt.setPattern(this.exprParser.expr());
1401             }
1402 
1403             if (lexer.token() == Token.WHERE) {
1404                 lexer.nextToken();
1405                 stmt.setWhere(this.exprParser.expr());
1406             }
1407 
1408             return stmt;
1409         }
1410 
1411         if (lexer.identifierEquals("COLLATION")) {
1412             lexer.nextToken();
1413             MySqlShowCollationStatement stmt = new MySqlShowCollationStatement();
1414 
1415             if (lexer.token() == Token.LIKE) {
1416                 lexer.nextToken();
1417                 stmt.setPattern(this.exprParser.expr());
1418             }
1419 
1420             if (lexer.token() == Token.WHERE) {
1421                 lexer.nextToken();
1422                 stmt.setWhere(this.exprParser.expr());
1423             }
1424 
1425             return stmt;
1426         }
1427 
1428         if (lexer.identifierEquals(BINLOG)) {
1429             lexer.nextToken();
1430             acceptIdentifier(EVENTS);
1431             MySqlShowBinLogEventsStatement stmt = new MySqlShowBinLogEventsStatement();
1432 
1433             if (lexer.token() == Token.IN) {
1434                 lexer.nextToken();
1435                 stmt.setIn(this.exprParser.expr());
1436             }
1437 
1438             if (lexer.token() == Token.FROM) {
1439                 lexer.nextToken();
1440                 stmt.setFrom(this.exprParser.expr());
1441             }
1442 
1443             stmt.setLimit(this.exprParser.parseLimit());
1444 
1445             return stmt;
1446         }
1447 
1448         if (lexer.identifierEquals("CONTRIBUTORS")) {
1449             lexer.nextToken();
1450             return new MySqlShowContributorsStatement();
1451         }
1452 
1453         if (lexer.token() == Token.CREATE) {
1454             lexer.nextToken();
1455 
1456             if (lexer.token() == Token.DATABASE) {
1457                 lexer.nextToken();
1458 
1459                 MySqlShowCreateDatabaseStatement stmt = new MySqlShowCreateDatabaseStatement();
1460                 stmt.setDatabase(this.exprParser.name());
1461                 return stmt;
1462             }
1463 
1464             if (lexer.identifierEquals("EVENT")) {
1465                 lexer.nextToken();
1466 
1467                 MySqlShowCreateEventStatement stmt = new MySqlShowCreateEventStatement();
1468                 stmt.setEventName(this.exprParser.name());
1469                 return stmt;
1470             }
1471 
1472             if (lexer.token() == Token.FUNCTION) {
1473                 lexer.nextToken();
1474 
1475                 MySqlShowCreateFunctionStatement stmt = new MySqlShowCreateFunctionStatement();
1476                 stmt.setName(this.exprParser.name());
1477                 return stmt;
1478             }
1479 
1480             if (lexer.token() == Token.PROCEDURE) {
1481                 lexer.nextToken();
1482 
1483                 MySqlShowCreateProcedureStatement stmt = new MySqlShowCreateProcedureStatement();
1484                 stmt.setName(this.exprParser.name());
1485                 return stmt;
1486             }
1487 
1488             if (lexer.token() == Token.TABLE) {
1489                 lexer.nextToken();
1490 
1491                 MySqlShowCreateTableStatement stmt = new MySqlShowCreateTableStatement();
1492                 stmt.setName(this.exprParser.name());
1493                 return stmt;
1494             }
1495 
1496             if (lexer.token() == Token.VIEW) {
1497                 lexer.nextToken();
1498 
1499                 MySqlShowCreateViewStatement stmt = new MySqlShowCreateViewStatement();
1500                 stmt.setName(this.exprParser.name());
1501                 return stmt;
1502             }
1503 
1504             if (lexer.token() == Token.TRIGGER) {
1505                 lexer.nextToken();
1506 
1507                 MySqlShowCreateTriggerStatement stmt = new MySqlShowCreateTriggerStatement();
1508                 stmt.setName(this.exprParser.name());
1509                 return stmt;
1510             }
1511 
1512             throw new ParserException("TODO " ~ lexer.info());
1513         }
1514 
1515         if (lexer.identifierEquals(ENGINE)) {
1516             lexer.nextToken();
1517             MySqlShowEngineStatement stmt = new MySqlShowEngineStatement();
1518             stmt.setName(this.exprParser.name());
1519             stmt.setOption(MySqlShowEngineStatement.Option(toUpper(lexer.stringVal())));
1520             lexer.nextToken();
1521             return stmt;
1522         }
1523 
1524         if (lexer.identifierEquals("STORAGE")) {
1525             lexer.nextToken();
1526             acceptIdentifier(ENGINES);
1527             MySqlShowEnginesStatement stmt = new MySqlShowEnginesStatement();
1528             stmt.setStorage(true);
1529             return stmt;
1530         }
1531 
1532         if (lexer.identifierEquals(ENGINES)) {
1533             lexer.nextToken();
1534             MySqlShowEnginesStatement stmt = new MySqlShowEnginesStatement();
1535             return stmt;
1536         }
1537 
1538         if (lexer.identifierEquals(EVENTS)) {
1539             lexer.nextToken();
1540             MySqlShowEventsStatement stmt = new MySqlShowEventsStatement();
1541 
1542             if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
1543                 lexer.nextToken();
1544                 stmt.setSchema(this.exprParser.name());
1545             }
1546 
1547             if (lexer.token() == Token.LIKE) {
1548                 lexer.nextToken();
1549                 stmt.setLike(this.exprParser.expr());
1550             }
1551 
1552             if (lexer.token() == Token.WHERE) {
1553                 lexer.nextToken();
1554                 stmt.setWhere(this.exprParser.expr());
1555             }
1556             return stmt;
1557         }
1558 
1559         if (lexer.token() == Token.FUNCTION) {
1560             lexer.nextToken();
1561 
1562             if (lexer.identifierEquals("CODE")) {
1563                 lexer.nextToken();
1564                 MySqlShowFunctionCodeStatement stmt = new MySqlShowFunctionCodeStatement();
1565                 stmt.setName(this.exprParser.name());
1566                 return stmt;
1567             }
1568 
1569             acceptIdentifier(STATUS);
1570             MySqlShowFunctionStatusStatement stmt = new MySqlShowFunctionStatusStatement();
1571 
1572             if (lexer.token() == Token.LIKE) {
1573                 lexer.nextToken();
1574                 stmt.setLike(this.exprParser.expr());
1575             }
1576 
1577             if (lexer.token() == Token.WHERE) {
1578                 lexer.nextToken();
1579                 stmt.setWhere(this.exprParser.expr());
1580             }
1581             return stmt;
1582         }
1583 
1584         // MySqlShowFunctionStatusStatement
1585 
1586         if (lexer.identifierEquals(ENGINE)) {
1587             lexer.nextToken();
1588             MySqlShowEngineStatement stmt = new MySqlShowEngineStatement();
1589             stmt.setName(this.exprParser.name());
1590             stmt.setOption(MySqlShowEngineStatement.Option(toUpper(lexer.stringVal())));
1591             lexer.nextToken();
1592             return stmt;
1593         }
1594 
1595         if (lexer.identifierEquals("STORAGE")) {
1596             lexer.nextToken();
1597             accept(Token.EQ);
1598             accept(Token.DEFAULT);
1599             MySqlShowEnginesStatement stmt = new MySqlShowEnginesStatement();
1600             stmt.setStorage(true);
1601             return stmt;
1602         }
1603 
1604         if (lexer.identifierEquals(ENGINES)) {
1605             lexer.nextToken();
1606             MySqlShowEnginesStatement stmt = new MySqlShowEnginesStatement();
1607             return stmt;
1608         }
1609 
1610         if (lexer.identifierEquals("GRANTS")) {
1611             lexer.nextToken();
1612             MySqlShowGrantsStatement stmt = new MySqlShowGrantsStatement();
1613 
1614             if (lexer.token() == Token.FOR) {
1615                 lexer.nextToken();
1616                 stmt.setUser(this.exprParser.expr());
1617             }
1618 
1619             return stmt;
1620         }
1621 
1622         if (lexer.token() == Token.INDEX || lexer.identifierEquals("INDEXES")) {
1623             lexer.nextToken();
1624             MySqlShowIndexesStatement stmt = new MySqlShowIndexesStatement();
1625 
1626             if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
1627                 lexer.nextToken();
1628                 SQLName table = exprParser.name();
1629                 stmt.setTable(table);
1630 
1631                 if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
1632                     lexer.nextToken();
1633                     SQLName database = exprParser.name();
1634                     stmt.setDatabase(database);
1635                 }
1636             }
1637 
1638             if (lexer.token() == Token.HINT) {
1639                 stmt.setHints(this.exprParser.parseHints());
1640             }
1641 
1642             return stmt;
1643         }
1644 
1645         if (lexer.identifierEquals("KEYS")) {
1646             lexer.nextToken();
1647             MySqlShowKeysStatement stmt = new MySqlShowKeysStatement();
1648 
1649             if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
1650                 lexer.nextToken();
1651                 SQLName table = exprParser.name();
1652                 stmt.setTable(table);
1653 
1654                 if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
1655                     lexer.nextToken();
1656                     SQLName database = exprParser.name();
1657                     stmt.setDatabase(database);
1658                 }
1659             }
1660 
1661             return stmt;
1662         }
1663 
1664         if (lexer.token() == Token.OPEN || lexer.identifierEquals("OPEN")) {
1665             lexer.nextToken();
1666             acceptIdentifier(TABLES);
1667             MySqlShowOpenTablesStatement stmt = new MySqlShowOpenTablesStatement();
1668 
1669             if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
1670                 lexer.nextToken();
1671                 stmt.setDatabase(this.exprParser.name());
1672             }
1673 
1674             if (lexer.token() == Token.LIKE) {
1675                 lexer.nextToken();
1676                 stmt.setLike(this.exprParser.expr());
1677             }
1678 
1679             if (lexer.token() == Token.WHERE) {
1680                 lexer.nextToken();
1681                 stmt.setWhere(this.exprParser.expr());
1682             }
1683             return stmt;
1684         }
1685 
1686         if (lexer.identifierEquals("PLUGINS")) {
1687             lexer.nextToken();
1688             MySqlShowPluginsStatement stmt = new MySqlShowPluginsStatement();
1689             return stmt;
1690         }
1691 
1692         if (lexer.identifierEquals("PRIVILEGES")) {
1693             lexer.nextToken();
1694             MySqlShowPrivilegesStatement stmt = new MySqlShowPrivilegesStatement();
1695             return stmt;
1696         }
1697 
1698         if (lexer.token() == Token.PROCEDURE) {
1699             lexer.nextToken();
1700 
1701             if (lexer.identifierEquals("CODE")) {
1702                 lexer.nextToken();
1703                 MySqlShowProcedureCodeStatement stmt = new MySqlShowProcedureCodeStatement();
1704                 stmt.setName(this.exprParser.name());
1705                 return stmt;
1706             }
1707 
1708             acceptIdentifier(STATUS);
1709             MySqlShowProcedureStatusStatement stmt = new MySqlShowProcedureStatusStatement();
1710 
1711             if (lexer.token() == Token.LIKE) {
1712                 lexer.nextToken();
1713                 stmt.setLike(this.exprParser.expr());
1714             }
1715 
1716             if (lexer.token() == Token.WHERE) {
1717                 lexer.nextToken();
1718                 stmt.setWhere(this.exprParser.expr());
1719             }
1720             return stmt;
1721         }
1722 
1723         if (lexer.identifierEquals("PROCESSLIST")) {
1724             lexer.nextToken();
1725             MySqlShowProcessListStatement stmt = new MySqlShowProcessListStatement();
1726             return stmt;
1727         }
1728 
1729         if (lexer.identifierEquals("PROFILES")) {
1730             lexer.nextToken();
1731             MySqlShowProfilesStatement stmt = new MySqlShowProfilesStatement();
1732             return stmt;
1733         }
1734 
1735         if (lexer.identifierEquals("PROFILE")) {
1736             lexer.nextToken();
1737             MySqlShowProfileStatement stmt = new MySqlShowProfileStatement();
1738 
1739             for (; ; ) {
1740                 if (lexer.token() == Token.ALL) {
1741                     stmt.getTypes().add(MySqlShowProfileStatement.Type.ALL);
1742                     lexer.nextToken();
1743                 } else if (lexer.identifierEquals("BLOCK")) {
1744                     lexer.nextToken();
1745                     acceptIdentifier("IO");
1746                     stmt.getTypes().add(MySqlShowProfileStatement.Type.BLOCK_IO);
1747                 } else if (lexer.identifierEquals("CONTEXT")) {
1748                     lexer.nextToken();
1749                     acceptIdentifier("SWITCHES");
1750                     stmt.getTypes().add(MySqlShowProfileStatement.Type.CONTEXT_SWITCHES);
1751                 } else if (lexer.identifierEquals("CPU")) {
1752                     lexer.nextToken();
1753                     stmt.getTypes().add(MySqlShowProfileStatement.Type.CPU);
1754                 } else if (lexer.identifierEquals("IPC")) {
1755                     lexer.nextToken();
1756                     stmt.getTypes().add(MySqlShowProfileStatement.Type.IPC);
1757                 } else if (lexer.identifierEquals("MEMORY")) {
1758                     lexer.nextToken();
1759                     stmt.getTypes().add(MySqlShowProfileStatement.Type.MEMORY);
1760                 } else if (lexer.identifierEquals("PAGE")) {
1761                     lexer.nextToken();
1762                     acceptIdentifier("FAULTS");
1763                     stmt.getTypes().add(MySqlShowProfileStatement.Type.PAGE_FAULTS);
1764                 } else if (lexer.identifierEquals("SOURCE")) {
1765                     lexer.nextToken();
1766                     stmt.getTypes().add(MySqlShowProfileStatement.Type.SOURCE);
1767                 } else if (lexer.identifierEquals("SWAPS")) {
1768                     lexer.nextToken();
1769                     stmt.getTypes().add(MySqlShowProfileStatement.Type.SWAPS);
1770                 } else {
1771                     break;
1772                 }
1773 
1774                 if (lexer.token() == Token.COMMA) {
1775                     lexer.nextToken();
1776                     continue;
1777                 }
1778                 break;
1779             }
1780 
1781             if (lexer.token() == Token.FOR) {
1782                 lexer.nextToken();
1783                 acceptIdentifier("QUERY");
1784                 stmt.setForQuery(this.exprParser.primary());
1785             }
1786 
1787             stmt.setLimit(this.exprParser.parseLimit());
1788 
1789             return stmt;
1790         }
1791 
1792         if (lexer.identifierEquals("RELAYLOG")) {
1793             lexer.nextToken();
1794             acceptIdentifier(EVENTS);
1795             MySqlShowRelayLogEventsStatement stmt = new MySqlShowRelayLogEventsStatement();
1796 
1797             if (lexer.token() == Token.IN) {
1798                 lexer.nextToken();
1799                 stmt.setLogName(this.exprParser.primary());
1800             }
1801 
1802             if (lexer.token() == Token.FROM) {
1803                 lexer.nextToken();
1804                 stmt.setFrom(this.exprParser.primary());
1805             }
1806 
1807             stmt.setLimit(this.exprParser.parseLimit());
1808 
1809             return stmt;
1810         }
1811 
1812         if (lexer.identifierEquals("RELAYLOG")) {
1813             lexer.nextToken();
1814             acceptIdentifier(EVENTS);
1815             MySqlShowRelayLogEventsStatement stmt = new MySqlShowRelayLogEventsStatement();
1816 
1817             if (lexer.token() == Token.IN) {
1818                 lexer.nextToken();
1819                 stmt.setLogName(this.exprParser.primary());
1820             }
1821 
1822             if (lexer.token() == Token.FROM) {
1823                 lexer.nextToken();
1824                 stmt.setFrom(this.exprParser.primary());
1825             }
1826 
1827             stmt.setLimit(this.exprParser.parseLimit());
1828 
1829             return stmt;
1830         }
1831 
1832         if (lexer.identifierEquals("SLAVE")) {
1833             lexer.nextToken();
1834             if (lexer.identifierEquals(STATUS)) {
1835                 lexer.nextToken();
1836                 return new MySqlShowSlaveStatusStatement();
1837             } else {
1838                 acceptIdentifier("HOSTS");
1839                 MySqlShowSlaveHostsStatement stmt = new MySqlShowSlaveHostsStatement();
1840                 return stmt;
1841             }
1842         }
1843 
1844         if (lexer.token() == Token.TABLE) {
1845             lexer.nextToken();
1846             acceptIdentifier(STATUS);
1847             MySqlShowTableStatusStatement stmt = new MySqlShowTableStatusStatement();
1848             if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
1849                 lexer.nextToken();
1850                 stmt.setDatabase(this.exprParser.name());
1851             }
1852 
1853             if (lexer.token() == Token.LIKE) {
1854                 lexer.nextToken();
1855                 stmt.setLike(this.exprParser.expr());
1856             }
1857 
1858             if (lexer.token() == Token.WHERE) {
1859                 lexer.nextToken();
1860                 stmt.setWhere(this.exprParser.expr());
1861             }
1862 
1863             return stmt;
1864         }
1865 
1866         if (lexer.token() == Token.DATABASE) {
1867             lexer.nextToken();
1868             accept(Token.PARTITION);
1869             acceptIdentifier("STATUS");
1870             accept(Token.FOR);
1871             MySqlShowDatabasePartitionStatusStatement stmt = new MySqlShowDatabasePartitionStatusStatement();
1872             stmt.setDatabase(this.exprParser.name());
1873             return stmt;
1874         }
1875 
1876         if (lexer.identifierEquals("TRIGGERS")) {
1877             lexer.nextToken();
1878             MySqlShowTriggersStatement stmt = new MySqlShowTriggersStatement();
1879 
1880             if (lexer.token() == Token.FROM) {
1881                 lexer.nextToken();
1882                 SQLName database = exprParser.name();
1883                 stmt.setDatabase(database);
1884             }
1885 
1886             if (lexer.token() == Token.LIKE) {
1887                 lexer.nextToken();
1888                 SQLExpr like = exprParser.expr();
1889                 stmt.setLike(like);
1890             }
1891 
1892             if (lexer.token() == Token.WHERE) {
1893                 lexer.nextToken();
1894                 SQLExpr where = exprParser.expr();
1895                 stmt.setWhere(where);
1896             }
1897 
1898             return stmt;
1899         }
1900 
1901         // MySqlShowSlaveHostsStatement
1902         throw new ParserException("TODO " ~ lexer.info());
1903     }
1904 
1905     private MySqlShowStatusStatement parseShowStatus() {
1906         MySqlShowStatusStatement stmt = new MySqlShowStatusStatement();
1907 
1908         if (lexer.token() == Token.LIKE) {
1909             lexer.nextToken();
1910             SQLExpr like = exprParser.expr();
1911             stmt.setLike(like);
1912         }
1913 
1914         if (lexer.token() == Token.WHERE) {
1915             lexer.nextToken();
1916             SQLExpr where = exprParser.expr();
1917             stmt.setWhere(where);
1918         }
1919 
1920         return stmt;
1921     }
1922 
1923     private MySqlShowVariantsStatement parseShowVariants() {
1924         MySqlShowVariantsStatement stmt = new MySqlShowVariantsStatement();
1925 
1926         if (lexer.token() == Token.LIKE) {
1927             lexer.nextToken();
1928             SQLExpr like = exprParser.expr();
1929             stmt.setLike(like);
1930         }
1931 
1932         if (lexer.token() == Token.WHERE) {
1933             lexer.nextToken();
1934             SQLExpr where = exprParser.expr();
1935             stmt.setWhere(where);
1936         }
1937 
1938         return stmt;
1939     }
1940 
1941     private MySqlShowWarningsStatement parseShowWarnings() {
1942         MySqlShowWarningsStatement stmt = new MySqlShowWarningsStatement();
1943 
1944         stmt.setLimit(this.exprParser.parseLimit());
1945 
1946         return stmt;
1947     }
1948 
1949     private MySqlShowDatabasesStatement parseShowDatabases() {
1950         MySqlShowDatabasesStatement stmt = new MySqlShowDatabasesStatement();
1951 
1952         if (lexer.token() == Token.LIKE) {
1953             lexer.nextToken();
1954             SQLExpr like = exprParser.expr();
1955             stmt.setLike(like);
1956         }
1957 
1958         if (lexer.token() == Token.WHERE) {
1959             lexer.nextToken();
1960             SQLExpr where = exprParser.expr();
1961             stmt.setWhere(where);
1962         }
1963 
1964         return stmt;
1965     }
1966 
1967     private SQLShowTablesStatement parseShowTabless() {
1968         SQLShowTablesStatement stmt = new SQLShowTablesStatement();
1969 
1970         if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
1971             lexer.nextToken();
1972             SQLName database = exprParser.name();
1973             if (lexer.token() == Token.SUB && cast(SQLIdentifierExpr)(database) !is null) {
1974                 lexer.mark();
1975                 lexer.nextToken();
1976                 string strVal = lexer.stringVal();
1977                 lexer.nextToken();
1978                 if (cast(SQLIdentifierExpr)(database) !is null) {
1979                     SQLIdentifierExpr ident = cast(SQLIdentifierExpr) database;
1980                     database = new SQLIdentifierExpr(ident.getName() ~ "-" ~ strVal);
1981                 }
1982             }
1983             stmt.setDatabase(database);
1984         }
1985 
1986         if (lexer.token() == Token.LIKE) {
1987             lexer.nextToken();
1988             SQLExpr like = exprParser.expr();
1989             stmt.setLike(like);
1990         }
1991 
1992         if (lexer.token() == Token.WHERE) {
1993             lexer.nextToken();
1994             SQLExpr where = exprParser.expr();
1995             stmt.setWhere(where);
1996         }
1997 
1998         return stmt;
1999     }
2000 
2001     private MySqlShowColumnsStatement parseShowColumns() {
2002         MySqlShowColumnsStatement stmt = new MySqlShowColumnsStatement();
2003 
2004         if (lexer.token() == Token.FROM) {
2005             lexer.nextToken();
2006             SQLName table = exprParser.name();
2007             stmt.setTable(table);
2008 
2009             if (lexer.token() == Token.FROM || lexer.token() == Token.IN) {
2010                 lexer.nextToken();
2011                 SQLName database = exprParser.name();
2012                 stmt.setDatabase(database);
2013             }
2014         }
2015 
2016         if (lexer.token() == Token.LIKE) {
2017             lexer.nextToken();
2018             SQLExpr like = exprParser.expr();
2019             stmt.setLike(like);
2020         }
2021 
2022         if (lexer.token() == Token.WHERE) {
2023             lexer.nextToken();
2024             SQLExpr where = exprParser.expr();
2025             stmt.setWhere(where);
2026         }
2027 
2028         return stmt;
2029     }
2030 
2031     public SQLStartTransactionStatement parseStart() {
2032         acceptIdentifier("START");
2033         acceptIdentifier("TRANSACTION");
2034 
2035         SQLStartTransactionStatement stmt = new SQLStartTransactionStatement();
2036         stmt.setDbType(dbType);
2037 
2038         if (lexer.token() == Token.WITH) {
2039             lexer.nextToken();
2040             acceptIdentifier("CONSISTENT");
2041             acceptIdentifier("SNAPSHOT");
2042             stmt.setConsistentSnapshot(true);
2043         }
2044 
2045         if (lexer.token() == Token.BEGIN) {
2046             lexer.nextToken();
2047             stmt.setBegin(true);
2048             if (lexer.identifierEquals("WORK")) {
2049                 lexer.nextToken();
2050                 stmt.setWork(true);
2051             }
2052         }
2053 
2054         if (lexer.token() == Token.HINT) {
2055             stmt.setHints(this.exprParser.parseHints());
2056         }
2057 
2058         return stmt;
2059     }
2060 
2061     override
2062     public SQLRollbackStatement parseRollback() {
2063         acceptIdentifier("ROLLBACK");
2064 
2065         SQLRollbackStatement stmt = new SQLRollbackStatement();
2066 
2067         if (lexer.identifierEquals("WORK")) {
2068             lexer.nextToken();
2069         }
2070 
2071         if (lexer.token() == Token.AND) {
2072             lexer.nextToken();
2073             if (lexer.token() == Token.NOT) {
2074                 lexer.nextToken();
2075                 acceptIdentifier(CHAIN);
2076                 stmt.setChain(Boolean.FALSE);
2077             } else {
2078                 acceptIdentifier(CHAIN);
2079                 stmt.setChain(Boolean.TRUE);
2080             }
2081         }
2082 
2083         if (lexer.token() == Token.TO) {
2084             lexer.nextToken();
2085 
2086             if (lexer.identifierEquals("SAVEPOINT")) {
2087                 lexer.nextToken();
2088             }
2089 
2090             stmt.setTo(this.exprParser.name());
2091         }
2092 
2093         return stmt;
2094     }
2095 
2096     override public SQLStatement parseCommit() {
2097         acceptIdentifier("COMMIT");
2098 
2099         SQLCommitStatement stmt = new SQLCommitStatement();
2100 
2101         if (lexer.identifierEquals("WORK")) {
2102             lexer.nextToken();
2103             stmt.setWork(true);
2104         }
2105 
2106         if (lexer.token() == Token.AND) {
2107             lexer.nextToken();
2108             if (lexer.token() == Token.NOT) {
2109                 lexer.nextToken();
2110                 acceptIdentifier(CHAIN);
2111                 stmt.setChain(Boolean.FALSE);
2112             } else {
2113                 acceptIdentifier(CHAIN);
2114                 stmt.setChain(Boolean.TRUE);
2115             }
2116         }
2117 
2118         return stmt;
2119     }
2120 
2121     public SQLReplaceStatement parseReplace() {
2122         SQLReplaceStatement stmt = new SQLReplaceStatement();
2123 
2124         accept(Token.REPLACE);
2125 
2126         if (lexer.token() == Token.COMMENT) {
2127             lexer.nextToken();
2128         }
2129 
2130         if (lexer.identifierEquals(FnvHash.Constants.LOW_PRIORITY)) {
2131             stmt.setLowPriority(true);
2132             lexer.nextToken();
2133         }
2134 
2135         if (lexer.identifierEquals(FnvHash.Constants.DELAYED)) {
2136             stmt.setDelayed(true);
2137             lexer.nextToken();
2138         }
2139 
2140         if (lexer.token() == Token.INTO) {
2141             lexer.nextToken();
2142         }
2143 
2144         SQLName tableName = exprParser.name();
2145         stmt.setTableName(tableName);
2146 
2147         if (lexer.token() == Token.LPAREN) {
2148             lexer.nextToken();
2149             if (lexer.token() == Token.SELECT) {
2150                 SQLQueryExpr queryExpr = cast(SQLQueryExpr) this.exprParser.expr();
2151                 stmt.setQuery(queryExpr);
2152             } else {
2153                 this.exprParser.exprList(stmt.getColumns(), stmt);
2154             }
2155             accept(Token.RPAREN);
2156         }
2157 
2158         if (lexer.token() == Token.VALUES || lexer.identifierEquals("VALUE")) {
2159             lexer.nextToken();
2160 
2161             parseValueClause(stmt.getValuesList(), 0, stmt);
2162         } else if (lexer.token() == Token.SELECT) {
2163             SQLQueryExpr queryExpr = cast(SQLQueryExpr) this.exprParser.expr();
2164             stmt.setQuery(queryExpr);
2165         } else if (lexer.token() == Token.SET) {
2166             lexer.nextToken();
2167 
2168             ValuesClause values = new ValuesClause();
2169             values.setParent(stmt);
2170             stmt.getValuesList().add(values);
2171             for (; ; ) {
2172                 stmt.addColumn(this.exprParser.name());
2173                 if (lexer.token() == Token.COLONEQ) {
2174                     lexer.nextToken();
2175                 } else {
2176                     accept(Token.EQ);
2177                 }
2178                 values.addValue(this.exprParser.expr());
2179 
2180                 if (lexer.token() == (Token.COMMA)) {
2181                     lexer.nextToken();
2182                     continue;
2183                 }
2184 
2185                 break;
2186             }
2187         } else if (lexer.token() == Token.LPAREN) {
2188             SQLSelect select = this.createSQLSelectParser().select();
2189             SQLQueryExpr queryExpr = new SQLQueryExpr(select);
2190             stmt.setQuery(queryExpr);
2191         }
2192 
2193         return stmt;
2194     }
2195 
2196     protected SQLStatement parseLoad() {
2197         acceptIdentifier("LOAD");
2198 
2199         if (lexer.identifierEquals("DATA")) {
2200             SQLStatement stmt = parseLoadDataInFile();
2201             return stmt;
2202         }
2203 
2204         if (lexer.identifierEquals("XML")) {
2205             SQLStatement stmt = parseLoadXml();
2206             return stmt;
2207         }
2208 
2209         throw new ParserException("TODO. " ~ lexer.info());
2210     }
2211 
2212     protected MySqlLoadXmlStatement parseLoadXml() {
2213         acceptIdentifier("XML");
2214 
2215         MySqlLoadXmlStatement stmt = new MySqlLoadXmlStatement();
2216 
2217         if (lexer.identifierEquals(FnvHash.Constants.LOW_PRIORITY)) {
2218             stmt.setLowPriority(true);
2219             lexer.nextToken();
2220         }
2221 
2222         if (lexer.identifierEquals("CONCURRENT")) {
2223             stmt.setConcurrent(true);
2224             lexer.nextToken();
2225         }
2226 
2227         if (lexer.identifierEquals(LOCAL)) {
2228             stmt.setLocal(true);
2229             lexer.nextToken();
2230         }
2231 
2232         acceptIdentifier("INFILE");
2233 
2234         SQLLiteralExpr fileName = cast(SQLLiteralExpr) exprParser.expr();
2235         stmt.setFileName(fileName);
2236 
2237         if (lexer.token() == Token.REPLACE) {
2238             stmt.setReplicate(true);
2239             lexer.nextToken();
2240         }
2241 
2242         if (lexer.identifierEquals(FnvHash.Constants.IGNORE)) {
2243             stmt.setIgnore(true);
2244             lexer.nextToken();
2245         }
2246 
2247         accept(Token.INTO);
2248         accept(Token.TABLE);
2249 
2250         SQLName tableName = exprParser.name();
2251         stmt.setTableName(tableName);
2252 
2253         if (lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
2254             lexer.nextToken();
2255             accept(Token.SET);
2256 
2257             if (lexer.token() != Token.LITERAL_CHARS) {
2258                 throw new ParserException("syntax error, illegal charset. "  ~ lexer.info());
2259             }
2260 
2261             string charset = lexer.stringVal();
2262             lexer.nextToken();
2263             stmt.setCharset(charset);
2264         }
2265 
2266         if (lexer.identifierEquals("ROWS")) {
2267             lexer.nextToken();
2268             accept(Token.IDENTIFIED);
2269             accept(Token.BY);
2270             SQLExpr rowsIdentifiedBy = exprParser.expr();
2271             stmt.setRowsIdentifiedBy(rowsIdentifiedBy);
2272         }
2273 
2274         if (lexer.identifierEquals(FnvHash.Constants.IGNORE)) {
2275             throw new ParserException("TODO. " ~ lexer.info());
2276         }
2277 
2278         if (lexer.token() == Token.SET) {
2279             throw new ParserException("TODO. " ~ lexer.info());
2280         }
2281 
2282         return stmt;
2283     }
2284 
2285     protected MySqlLoadDataInFileStatement parseLoadDataInFile() {
2286 
2287         acceptIdentifier("DATA");
2288 
2289         MySqlLoadDataInFileStatement stmt = new MySqlLoadDataInFileStatement();
2290 
2291         if (lexer.identifierEquals(FnvHash.Constants.LOW_PRIORITY)) {
2292             stmt.setLowPriority(true);
2293             lexer.nextToken();
2294         }
2295 
2296         if (lexer.identifierEquals("CONCURRENT")) {
2297             stmt.setConcurrent(true);
2298             lexer.nextToken();
2299         }
2300 
2301         if (lexer.identifierEquals(LOCAL)) {
2302             stmt.setLocal(true);
2303             lexer.nextToken();
2304         }
2305 
2306         acceptIdentifier("INFILE");
2307 
2308         SQLLiteralExpr fileName = cast(SQLLiteralExpr) exprParser.expr();
2309         stmt.setFileName(fileName);
2310 
2311         if (lexer.token() == Token.REPLACE) {
2312             stmt.setReplicate(true);
2313             lexer.nextToken();
2314         }
2315 
2316         if (lexer.identifierEquals(FnvHash.Constants.IGNORE)) {
2317             stmt.setIgnore(true);
2318             lexer.nextToken();
2319         }
2320 
2321         accept(Token.INTO);
2322         accept(Token.TABLE);
2323 
2324         SQLName tableName = exprParser.name();
2325         stmt.setTableName(tableName);
2326 
2327         if (lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
2328             lexer.nextToken();
2329             accept(Token.SET);
2330 
2331             if (lexer.token() != Token.LITERAL_CHARS) {
2332                 throw new ParserException("syntax error, illegal charset. " ~ lexer.info());
2333             }
2334 
2335             string charset = lexer.stringVal();
2336             lexer.nextToken();
2337             stmt.setCharset(charset);
2338         }
2339 
2340         if (lexer.identifierEquals("FIELDS") || lexer.identifierEquals("COLUMNS")) {
2341             lexer.nextToken();
2342             if (lexer.identifierEquals("TERMINATED")) {
2343                 lexer.nextToken();
2344                 accept(Token.BY);
2345                 stmt.setColumnsTerminatedBy(new SQLCharExpr(lexer.stringVal()));
2346                 lexer.nextToken();
2347             }
2348 
2349             if (lexer.identifierEquals("OPTIONALLY")) {
2350                 stmt.setColumnsEnclosedOptionally(true);
2351                 lexer.nextToken();
2352             }
2353 
2354             if (lexer.identifierEquals("ENCLOSED")) {
2355                 lexer.nextToken();
2356                 accept(Token.BY);
2357                 stmt.setColumnsEnclosedBy(new SQLCharExpr(lexer.stringVal()));
2358                 lexer.nextToken();
2359             }
2360 
2361             if (lexer.identifierEquals("ESCAPED")) {
2362                 lexer.nextToken();
2363                 accept(Token.BY);
2364                 stmt.setColumnsEscaped(new SQLCharExpr(lexer.stringVal()));
2365                 lexer.nextToken();
2366             }
2367         }
2368 
2369         if (lexer.identifierEquals("LINES")) {
2370             lexer.nextToken();
2371             if (lexer.identifierEquals("STARTING")) {
2372                 lexer.nextToken();
2373                 accept(Token.BY);
2374                 stmt.setLinesStartingBy(new SQLCharExpr(lexer.stringVal()));
2375                 lexer.nextToken();
2376             }
2377 
2378             if (lexer.identifierEquals("TERMINATED")) {
2379                 lexer.nextToken();
2380                 accept(Token.BY);
2381                 stmt.setLinesTerminatedBy(new SQLCharExpr(lexer.stringVal()));
2382                 lexer.nextToken();
2383             }
2384         }
2385 
2386         if (lexer.identifierEquals(FnvHash.Constants.IGNORE)) {
2387             lexer.nextToken();
2388             stmt.setIgnoreLinesNumber(this.exprParser.expr());
2389             acceptIdentifier("LINES");
2390         }
2391 
2392         if (lexer.token() == Token.LPAREN) {
2393             lexer.nextToken();
2394             this.exprParser.exprList(stmt.getColumns(), stmt);
2395             accept(Token.RPAREN);
2396         }
2397 
2398         if (lexer.token() == Token.SET) {
2399             lexer.nextToken();
2400             this.exprParser.exprList(stmt.getSetList(), stmt);
2401         }
2402 
2403         return stmt;
2404 
2405     }
2406 
2407     public MySqlPrepareStatement parsePrepare() {
2408         acceptIdentifier("PREPARE");
2409 
2410         SQLName name = exprParser.name();
2411         accept(Token.FROM);
2412         SQLExpr from = exprParser.expr();
2413 
2414         return new MySqlPrepareStatement(name, from);
2415     }
2416 
2417     public MySqlExecuteStatement parseExecute() {
2418         acceptIdentifier("EXECUTE");
2419 
2420         MySqlExecuteStatement stmt = new MySqlExecuteStatement();
2421 
2422         SQLName statementName = exprParser.name();
2423         stmt.setStatementName(statementName);
2424 
2425         if (lexer.identifierEquals("USING")) {
2426             lexer.nextToken();
2427             exprParser.exprList(stmt.getParameters(), stmt);
2428         } else if (lexer.token() == Token.IDENTIFIER) {
2429             exprParser.exprList(stmt.getParameters(), stmt);
2430         }
2431 
2432         return stmt;
2433     }
2434 
2435     public MysqlDeallocatePrepareStatement parseDeallocatePrepare() {
2436         acceptIdentifier("DEALLOCATE");
2437         acceptIdentifier("PREPARE");
2438 
2439         MysqlDeallocatePrepareStatement stmt = new MysqlDeallocatePrepareStatement();
2440         SQLName statementName = exprParser.name();
2441         stmt.setStatementName(statementName);
2442 
2443         return stmt;
2444     }
2445 
2446     override public SQLInsertStatement parseInsert() {
2447         MySqlInsertStatement stmt = new MySqlInsertStatement();
2448 
2449         SQLName tableName = null;
2450         if (lexer.token() == Token.INSERT) {
2451             lexer.nextToken();
2452 
2453             for (; ; ) {
2454                 if (lexer.token() == Token.IDENTIFIER) {
2455                     long hash = lexer.hash_lower();
2456 
2457                     if (hash == FnvHash.Constants.LOW_PRIORITY) {
2458                         stmt.setLowPriority(true);
2459                         lexer.nextToken();
2460                         continue;
2461                     }
2462 
2463                     if (hash == FnvHash.Constants.DELAYED) {
2464                         stmt.setDelayed(true);
2465                         lexer.nextToken();
2466                         continue;
2467                     }
2468 
2469                     if (hash == FnvHash.Constants.HIGH_PRIORITY) {
2470                         stmt.setHighPriority(true);
2471                         lexer.nextToken();
2472                         continue;
2473                     }
2474 
2475                     if (hash == FnvHash.Constants.IGNORE) {
2476                         stmt.setIgnore(true);
2477                         lexer.nextToken();
2478                         continue;
2479                     }
2480 
2481                     if (hash == FnvHash.Constants.ROLLBACK_ON_FAIL) {
2482                         stmt.setRollbackOnFail(true);
2483                         lexer.nextToken();
2484                         continue;
2485                     }
2486                 }
2487 
2488 
2489                 break;
2490             }
2491 
2492             if (lexer.token() == Token.INTO) {
2493                 lexer.nextToken();
2494             }
2495 
2496             if (lexer.token() == Token.LINE_COMMENT) {
2497                 lexer.nextToken();
2498             }
2499 
2500             tableName = this.exprParser.name();
2501             stmt.setTableName(tableName);
2502 
2503             if (lexer.token() == Token.HINT) {
2504                 string comment = "/*" ~ lexer.stringVal() ~ "*/";
2505                 lexer.nextToken();
2506                 stmt.getTableSource().addAfterComment(comment);
2507             }
2508 
2509             if (lexer.token() == Token.IDENTIFIER
2510                     && !lexer.identifierEquals(FnvHash.Constants.VALUE)) {
2511                 stmt.setAlias(lexer.stringVal());
2512                 lexer.nextToken();
2513             }
2514 
2515         }
2516 
2517         int columnSize = 0;
2518         if (lexer.token() == Token.LPAREN) {
2519             bool useInsertColumnsCache = lexer.isEnabled(SQLParserFeature.UseInsertColumnsCache);
2520             InsertColumnsCache insertColumnsCache = null;
2521 
2522             InsertColumnsCache.Entry cachedColumns = null;
2523             if (useInsertColumnsCache) {
2524                 insertColumnsCache = this.insertColumnsCache;
2525                 if (insertColumnsCache is null) {
2526                     insertColumnsCache =  InsertColumnsCache.global;
2527                 }
2528 
2529                 if (tableName !is null) {
2530                     cachedColumns = insertColumnsCache.get(tableName.hashCode64());
2531                 }
2532             }
2533 
2534             int pos = lexer.pos();
2535             if (cachedColumns !is null
2536                     && lexer.text.startsWith(cachedColumns.columnsString, pos)) {
2537                 if (!lexer.isEnabled(SQLParserFeature.OptimizedForParameterized)) {
2538                     List!(SQLExpr) columns = stmt.getColumns();
2539                     List!(SQLExpr) cachedColumns2 = cachedColumns.columns;
2540                     for (int i = 0, size = cachedColumns2.size(); i < size; i++) {
2541                         columns.add(cachedColumns2.get(i).clone());
2542                     }
2543                 }
2544                 stmt.setColumnsString(cachedColumns.columnsFormattedString, cachedColumns.columnsFormattedStringHash);
2545                 int p2 = pos + cast(int)(cachedColumns.columnsString.length);
2546                 lexer.reset(p2);
2547                 lexer.nextToken();
2548             } else {
2549                 lexer.nextToken();
2550                 if (lexer.token() == Token.SELECT) {
2551                     SQLSelect select = this.exprParser.createSelectParser().select();
2552                     select.setParent(stmt);
2553                     stmt.setQuery(select);
2554                 } else {
2555                     List!(SQLExpr) columns = stmt.getColumns();
2556 
2557                     if (lexer.token() != Token.RPAREN) {
2558                         for (; ; ) {
2559                             string identName;
2560                             long hash;
2561 
2562                             Token token = lexer.token();
2563                             if (token == Token.IDENTIFIER) {
2564                                 identName = lexer.stringVal();
2565                                 hash = lexer.hash_lower();
2566                             } else if (token == Token.LITERAL_CHARS) {
2567                                 identName = '\'' ~ lexer.stringVal() ~ '\'';
2568                                 hash = 0;
2569                             } else {
2570                                 identName = lexer.stringVal();
2571                                 hash = 0;
2572                             }
2573                             lexer.nextTokenComma();
2574                             SQLExpr expr = new SQLIdentifierExpr(identName, hash);
2575                             while (lexer.token() == Token.DOT) {
2576                                 lexer.nextToken();
2577                                 string propertyName = lexer.stringVal();
2578                                 lexer.nextToken();
2579                                 expr = new SQLPropertyExpr(expr, propertyName);
2580                             }
2581 
2582                             expr.setParent(stmt);
2583                             columns.add(expr);
2584                             columnSize++;
2585 
2586                             if (lexer.token() == Token.COMMA) {
2587                                 lexer.nextTokenIdent();
2588                                 continue;
2589                             }
2590 
2591                             break;
2592                         }
2593                         columnSize = stmt.getColumns().size();
2594 
2595                         if (insertColumnsCache !is null && tableName !is null) {
2596                             string columnsString = lexer.subString(pos, lexer.pos() - pos);
2597 
2598                             List!(SQLExpr) clonedColumns = new ArrayList!(SQLExpr)(columnSize);
2599                             for (int i = 0; i < columns.size(); i++) {
2600                                 clonedColumns.add(columns.get(i).clone());
2601                             }
2602 
2603                             StringBuilder buf = new StringBuilder();
2604                             SQLASTOutputVisitor outputVisitor = SQLUtils.createOutputVisitor(buf, dbType);
2605                             outputVisitor.printInsertColumns(columns);
2606 
2607                             string formattedColumnsString = buf.toString();
2608                             long columnsFormattedStringHash = FnvHash.fnv1a_64_lower(formattedColumnsString);
2609 
2610                             insertColumnsCache.put(tableName.hashCode64(), columnsString, formattedColumnsString, clonedColumns);
2611                             stmt.setColumnsString(formattedColumnsString, columnsFormattedStringHash);
2612                         }
2613                     }
2614                 }
2615                 accept(Token.RPAREN);
2616             }
2617         }
2618 
2619         if (lexer.token() == Token.LINE_COMMENT) {
2620             lexer.nextToken();
2621         }
2622 
2623         if (lexer.token() == Token.VALUES || lexer.identifierEquals(FnvHash.Constants.VALUE)) {
2624             lexer.nextTokenLParen();
2625             parseValueClause(stmt.getValuesList(), columnSize, stmt);
2626         } else if (lexer.token() == Token.SET) {
2627             lexer.nextToken();
2628 
2629             ValuesClause values = new ValuesClause();
2630             stmt.addValueCause(values);
2631 
2632             for (; ; ) {
2633                 SQLName name = this.exprParser.name();
2634                 stmt.addColumn(name);
2635                 if (lexer.token() == Token.EQ) {
2636                     lexer.nextToken();
2637                 } else {
2638                     accept(Token.COLONEQ);
2639                 }
2640                 values.addValue(this.exprParser.expr());
2641 
2642                 if (lexer.token() == Token.COMMA) {
2643                     lexer.nextToken();
2644                     continue;
2645                 }
2646 
2647                 break;
2648             }
2649 
2650         } else if (lexer.token() == (Token.SELECT)) {
2651             SQLSelect select = this.exprParser.createSelectParser().select();
2652             select.setParent(stmt);
2653             stmt.setQuery(select);
2654         } else if (lexer.token() == (Token.LPAREN)) {
2655             lexer.nextToken();
2656             SQLSelect select = this.exprParser.createSelectParser().select();
2657             select.setParent(stmt);
2658             stmt.setQuery(select);
2659             accept(Token.RPAREN);
2660         }
2661 
2662         if (lexer.token() == Token.ON) {
2663             lexer.nextToken();
2664             acceptIdentifier("DUPLICATE");
2665             accept(Token.KEY);
2666             accept(Token.UPDATE);
2667 
2668             List!(SQLExpr) duplicateKeyUpdate = stmt.getDuplicateKeyUpdate();
2669             for (;;) {
2670                 SQLName name = this.exprParser.name();
2671                 accept(Token.EQ);
2672                 SQLExpr value;
2673                 try {
2674                     value = this.exprParser.expr();
2675                 } catch (EOFParserException e) {
2676                     throw new ParserException("EOF, " ~ name.stringof ~ "=", e);
2677                 }
2678 
2679                 SQLBinaryOpExpr assignment = new SQLBinaryOpExpr(name, SQLBinaryOperator.Equality, value);
2680                 assignment.setParent(stmt);
2681                 duplicateKeyUpdate.add(assignment);
2682 
2683                 if (lexer.token() == Token.COMMA) {
2684                     lexer.nextTokenIdent();
2685                     continue;
2686                 }
2687                 break;
2688             }
2689         }
2690 
2691         return stmt;
2692     }
2693 
2694     override public MySqlSelectParser createSQLSelectParser() {
2695         return new MySqlSelectParser(this.exprParser, selectListCache);
2696     }
2697 
2698     override public SQLStatement parseSet() {
2699         accept(Token.SET);
2700 
2701         if (lexer.identifierEquals(FnvHash.Constants.PASSWORD)) {
2702             lexer.nextToken();
2703             SQLSetStatement stmt = new SQLSetStatement();
2704             stmt.setDbType(dbType);
2705             stmt.setOption(SQLSetStatement.Option.PASSWORD);
2706 
2707             SQLExpr user = null;
2708             if (lexer.token() == Token.FOR) {
2709                 lexer.nextToken();
2710                 user = this.exprParser.name();
2711             }
2712 
2713             accept(Token.EQ);
2714 
2715             SQLExpr password = this.exprParser.expr();
2716 
2717             stmt.set(user, password);
2718 
2719             return stmt;
2720         }
2721 
2722         Boolean global = null;
2723         Boolean session = null;
2724         if (lexer.identifierEquals(GLOBAL)) {
2725             global = Boolean.TRUE;
2726             lexer.nextToken();
2727         } else if (lexer.identifierEquals(SESSION)) {
2728             global = Boolean.FALSE;
2729             session = Boolean.TRUE;
2730             lexer.nextToken();
2731         }
2732 
2733         if (lexer.identifierEquals("TRANSACTION")) {
2734             MySqlSetTransactionStatement stmt = new MySqlSetTransactionStatement();
2735             stmt.setGlobal(global);
2736             stmt.setSession(session);
2737 
2738             lexer.nextToken();
2739             if (lexer.identifierEquals("ISOLATION")) {
2740                 lexer.nextToken();
2741                 acceptIdentifier("LEVEL");
2742 
2743                 if (lexer.identifierEquals(READ)) {
2744                     lexer.nextToken();
2745 
2746                     if (lexer.identifierEquals("UNCOMMITTED")) {
2747                         stmt.setIsolationLevel("READ UNCOMMITTED");
2748                         lexer.nextToken();
2749                     } else if (lexer.identifierEquals(WRITE)) {
2750                         stmt.setIsolationLevel("READ WRITE");
2751                         lexer.nextToken();
2752                     } else if (lexer.identifierEquals("ONLY")) {
2753                         stmt.setIsolationLevel("READ ONLY");
2754                         lexer.nextToken();
2755                     } else if (lexer.identifierEquals("COMMITTED")) {
2756                         stmt.setIsolationLevel("READ COMMITTED");
2757                         lexer.nextToken();
2758                     } else {
2759                         throw new ParserException("UNKOWN TRANSACTION LEVEL : " ~ lexer.stringVal() ~ ", " ~ lexer.info());
2760                     }
2761                 } else if (lexer.identifierEquals("SERIALIZABLE")) {
2762                     stmt.setIsolationLevel("SERIALIZABLE");
2763                     lexer.nextToken();
2764                 } else if (lexer.identifierEquals("REPEATABLE")) {
2765                     lexer.nextToken();
2766                     if (lexer.identifierEquals(READ)) {
2767                         stmt.setIsolationLevel("REPEATABLE READ");
2768                         lexer.nextToken();
2769                     } else {
2770                         throw new ParserException("UNKOWN TRANSACTION LEVEL : " ~ lexer.stringVal() ~ ", " ~ lexer.info());
2771                     }
2772                 } else {
2773                     throw new ParserException("UNKOWN TRANSACTION LEVEL : " ~ lexer.stringVal() ~ ", " ~ lexer.info());
2774                 }
2775             } else if (lexer.identifierEquals(READ)) {
2776                 lexer.nextToken();
2777                 if (lexer.identifierEquals("ONLY")) {
2778                     stmt.setAccessModel("ONLY");
2779                     lexer.nextToken();
2780                 } else if (lexer.identifierEquals("WRITE")) {
2781                     stmt.setAccessModel("WRITE");
2782                     lexer.nextToken();
2783                 } else {
2784                     throw new ParserException("UNKOWN ACCESS MODEL : " ~ lexer.stringVal() ~ ", " ~ lexer.info());
2785                 }
2786             }
2787 
2788             return stmt;
2789 //        } else if (lexer.identifierEquals("NAMES")) {
2790 //            lexer.nextToken();
2791 //
2792 //            MySqlSetNamesStatement stmt = new MySqlSetNamesStatement();
2793 //            if (lexer.token() == Token.DEFAULT) {
2794 //                lexer.nextToken();
2795 //                stmt.setDefault(true);
2796 //            } else {
2797 //                string charSet = lexer.stringVal();
2798 //                stmt.setCharSet(charSet);
2799 //                lexer.nextToken();
2800 //                if (lexer.identifierEquals(COLLATE2)) {
2801 //                    lexer.nextToken();
2802 //
2803 //                    string collate = lexer.stringVal();
2804 //                    stmt.setCollate(collate);
2805 //                    lexer.nextToken();
2806 //                }
2807 //            }
2808 //            return stmt;
2809 //        } else if (lexer.identifierEquals(CHARACTER)) {
2810 //            lexer.nextToken();
2811 //
2812 //            accept(Token.SET);
2813 //
2814 //            MySqlSetCharSetStatement stmt = new MySqlSetCharSetStatement();
2815 //            if (lexer.token() == Token.DEFAULT) {
2816 //                lexer.nextToken();
2817 //                stmt.setDefault(true);
2818 //            } else {
2819 //                string charSet = lexer.stringVal();
2820 //                stmt.setCharSet(charSet);
2821 //                lexer.nextToken();
2822 //                if (lexer.identifierEquals(COLLATE2)) {
2823 //                    lexer.nextToken();
2824 //
2825 //                    string collate = lexer.stringVal();
2826 //                    stmt.setCollate(collate);
2827 //                    lexer.nextToken();
2828 //                }
2829 //            }
2830 //            return stmt;
2831         } else {
2832             SQLSetStatement stmt = new SQLSetStatement(getDbType());
2833 
2834             parseAssignItems(stmt.getItems(), stmt);
2835 
2836             if (global !is null && global) {
2837                 SQLVariantRefExpr varRef = cast(SQLVariantRefExpr) stmt.getItems().get(0).getTarget();
2838                 varRef.setGlobal(true);
2839             }
2840 
2841             if(session !is null && session){
2842                 SQLVariantRefExpr varRef = cast(SQLVariantRefExpr) stmt.getItems().get(0).getTarget();
2843                 varRef.setSession(true);
2844             }
2845 
2846             if (lexer.token() == Token.HINT) {
2847                 stmt.setHints(this.exprParser.parseHints());
2848             }
2849 
2850             return stmt;
2851         }
2852     }
2853 
2854     override public SQLStatement parseAlter() {
2855         accept(Token.ALTER);
2856 
2857         if (lexer.token() == Token.USER) {
2858             return parseAlterUser();
2859         }
2860 
2861         bool ignore = false;
2862 
2863         if (lexer.identifierEquals(FnvHash.Constants.IGNORE)) {
2864             ignore = true;
2865             lexer.nextToken();
2866         }
2867 
2868         if (lexer.token() == Token.TABLE) {
2869             return parseAlterTable(ignore);
2870         }
2871 
2872         if (lexer.token() == Token.DATABASE
2873                 || lexer.token() == Token.SCHEMA) {
2874             return parseAlterDatabase();
2875         }
2876 
2877         if (lexer.identifierEquals(FnvHash.Constants.EVENT)) {
2878             return parseAlterEvent();
2879         }
2880 
2881         if (lexer.token() == Token.FUNCTION) {
2882             return parseAlterFunction();
2883         }
2884 
2885         if (lexer.token() == Token.PROCEDURE) {
2886             return parseAlterProcedure();
2887         }
2888 
2889         if (lexer.token() == Token.TABLESPACE) {
2890             return parseAlterTableSpace();
2891         }
2892 
2893         if (lexer.token() == Token.VIEW) {
2894             return parseAlterView();
2895         }
2896 
2897         if (lexer.identifierEquals(FnvHash.Constants.LOGFILE)) {
2898             return parseAlterLogFileGroup();
2899         }
2900 
2901         if (lexer.identifierEquals(FnvHash.Constants.SERVER)) {
2902             return parseAlterServer();
2903         }
2904 
2905         if (lexer.identifierEquals(FnvHash.Constants.ALGORITHM)) {
2906             return parseAlterView();
2907         }
2908 
2909         if (lexer.identifierEquals(FnvHash.Constants.DEFINER)) {
2910             Lexer.SavePoint savePoint = lexer.mark();
2911             lexer.nextToken();
2912             accept(Token.EQ);
2913             this.getExprParser().userName();
2914             if (lexer.identifierEquals(FnvHash.Constants.EVENT)) {
2915                 lexer.reset(savePoint);
2916                 return parseAlterEvent();
2917             } else {
2918                 lexer.reset(savePoint);
2919                 return parseAlterView();
2920             }
2921         }
2922 
2923         throw new ParserException("TODO " ~ lexer.info());
2924     }
2925 
2926     protected SQLStatement parseAlterView() {
2927         if (lexer.token() == Token.ALTER) {
2928             lexer.nextToken();
2929         }
2930 
2931         SQLAlterViewStatement createView = new SQLAlterViewStatement(getDbType());
2932 
2933         if (lexer.identifierEquals("ALGORITHM")) {
2934             lexer.nextToken();
2935             accept(Token.EQ);
2936             string algorithm = lexer.stringVal();
2937             createView.setAlgorithm(algorithm);
2938             lexer.nextToken();
2939         }
2940 
2941         if (lexer.identifierEquals("DEFINER")) {
2942             lexer.nextToken();
2943             accept(Token.EQ);
2944             SQLName definer = cast(SQLName) this.exprParser.expr();
2945             createView.setDefiner(definer);
2946         }
2947 
2948         if (lexer.identifierEquals("SQL")) {
2949             lexer.nextToken();
2950             acceptIdentifier("SECURITY");
2951             string sqlSecurity = lexer.stringVal();
2952             createView.setSqlSecurity(sqlSecurity);
2953             lexer.nextToken();
2954         }
2955 
2956         if (lexer.identifierEquals("FORCE")) {
2957             lexer.nextToken();
2958             createView.setForce(true);
2959         }
2960 
2961         this.accept(Token.VIEW);
2962 
2963         if (lexer.token() == Token.IF || lexer.identifierEquals("IF")) {
2964             lexer.nextToken();
2965             accept(Token.NOT);
2966             accept(Token.EXISTS);
2967             createView.setIfNotExists(true);
2968         }
2969 
2970         createView.setName(exprParser.name());
2971 
2972         if (lexer.token() == Token.LPAREN) {
2973             lexer.nextToken();
2974 
2975             for (;;) {
2976 
2977                 if (lexer.token() == Token.CONSTRAINT) {
2978                     SQLTableConstraint constraint = cast(SQLTableConstraint) this.exprParser.parseConstaint();
2979                     createView.addColumn(constraint);
2980                 } else {
2981                     SQLColumnDefinition column = new SQLColumnDefinition();
2982                     column.setDbType(dbType);
2983                     SQLName expr = this.exprParser.name();
2984                     column.setName(expr);
2985 
2986                     this.exprParser.parseColumnRest(column);
2987 
2988                     if (lexer.token() == Token.COMMENT) {
2989                         lexer.nextToken();
2990 
2991                         SQLExpr comment;
2992                         if (lexer.token() == Token.LITERAL_ALIAS) {
2993                             string alias_p = lexer.stringVal();
2994                             if (alias_p.length > 2 && charAt(alias_p, 0) == '"' && charAt(alias_p, alias_p.length - 1) == '"') {
2995                                 alias_p = alias_p.substring(1, cast(int)(alias_p.length - 1));
2996                             }
2997                             comment = new SQLCharExpr(alias_p);
2998                             lexer.nextToken();
2999                         } else {
3000                             comment = this.exprParser.primary();
3001                         }
3002                         column.setComment(comment);
3003                     }
3004 
3005                     column.setParent(createView);
3006                     createView.addColumn(column);
3007                 }
3008 
3009                 if (lexer.token() == Token.COMMA) {
3010                     lexer.nextToken();
3011                 } else {
3012                     break;
3013                 }
3014             }
3015 
3016             accept(Token.RPAREN);
3017         }
3018 
3019         if (lexer.token() == Token.COMMENT) {
3020             lexer.nextToken();
3021             SQLCharExpr comment = cast(SQLCharExpr) exprParser.primary();
3022             createView.setComment(comment);
3023         }
3024 
3025         this.accept(Token.AS);
3026 
3027         SQLSelectParser selectParser = this.createSQLSelectParser();
3028         createView.setSubQuery(selectParser.select());
3029 
3030         if (lexer.token() == Token.WITH) {
3031             lexer.nextToken();
3032 
3033             if (lexer.identifierEquals("CASCADED")) {
3034                 createView.setWithCascaded(true);
3035                 lexer.nextToken();
3036             } else if (lexer.identifierEquals("LOCAL")){
3037                 createView.setWithLocal(true);
3038                 lexer.nextToken();
3039             } else if (lexer.identifierEquals("READ")) {
3040                 lexer.nextToken();
3041                 accept(Token.ONLY);
3042                 createView.setWithReadOnly(true);
3043             }
3044 
3045             if (lexer.token() == Token.CHECK) {
3046                 lexer.nextToken();
3047                 acceptIdentifier("OPTION");
3048                 createView.setWithCheckOption(true);
3049             }
3050         }
3051 
3052         return createView;
3053     }
3054 
3055     protected SQLStatement parseAlterTableSpace() {
3056         if (lexer.token() == Token.ALTER) {
3057             lexer.nextToken();
3058         }
3059 
3060         accept(Token.TABLESPACE);
3061 
3062         SQLName name = this.exprParser.name();
3063 
3064         MySqlAlterTablespaceStatement stmt = new MySqlAlterTablespaceStatement();
3065         stmt.setName(name);
3066 
3067         if (lexer.identifierEquals(FnvHash.Constants.ADD)) {
3068             lexer.nextToken();
3069             acceptIdentifier("DATAFILE");
3070             SQLExpr file = this.exprParser.primary();
3071             stmt.setAddDataFile(file);
3072         } else if (lexer.token() == Token.DROP) {
3073             lexer.nextToken();
3074             acceptIdentifier("DATAFILE");
3075             SQLExpr file = this.exprParser.primary();
3076             stmt.setDropDataFile(file);
3077         }
3078 
3079         if (lexer.identifierEquals(FnvHash.Constants.INITIAL_SIZE)) {
3080             lexer.nextToken();
3081             if (lexer.token() == Token.EQ) {
3082                 lexer.nextToken();
3083             }
3084             SQLExpr initialSize = this.exprParser.expr();
3085             stmt.setInitialSize(initialSize);
3086         }
3087 
3088         if (lexer.identifierEquals(FnvHash.Constants.WAIT)) {
3089             lexer.nextToken();
3090             stmt.setWait(true);
3091         }
3092 
3093         if (lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
3094             lexer.nextToken();
3095             if (lexer.token() == Token.EQ) {
3096                 lexer.nextToken();
3097             }
3098             SQLExpr engine = this.exprParser.expr();
3099             stmt.setEngine(engine);
3100         }
3101 
3102         return stmt;
3103     }
3104 
3105     protected SQLStatement parseAlterServer() {
3106         if (lexer.token() == Token.ALTER) {
3107             lexer.nextToken();
3108         }
3109         acceptIdentifier("SERVER");
3110 
3111         SQLName name = this.exprParser.name();
3112 
3113         MySqlAlterServerStatement stmt = new MySqlAlterServerStatement();
3114         stmt.setName(name);
3115 
3116         acceptIdentifier("OPTIONS");
3117         accept(Token.LPAREN);
3118         if (lexer.token() == Token.USER) {
3119             lexer.nextToken();
3120             SQLExpr user = this.exprParser.name();
3121             stmt.setUser(user);
3122         }
3123         accept(Token.RPAREN);
3124 
3125         return stmt;
3126     }
3127 
3128     protected SQLStatement parseCreateLogFileGroup() {
3129         if (lexer.token() == Token.ALTER) {
3130             lexer.nextToken();
3131         }
3132         acceptIdentifier("LOGFILE");
3133         accept(Token.GROUP);
3134 
3135         SQLName name = this.exprParser.name();
3136 
3137         MySqlCreateAddLogFileGroupStatement stmt = new MySqlCreateAddLogFileGroupStatement();
3138         stmt.setName(name);
3139 
3140         acceptIdentifier("ADD");
3141         acceptIdentifier("UNDOFILE");
3142 
3143         SQLExpr fileName = this.exprParser.primary();
3144         stmt.setAddUndoFile(fileName);
3145 
3146         if (lexer.identifierEquals(FnvHash.Constants.INITIAL_SIZE)) {
3147             lexer.nextToken();
3148             if (lexer.token() == Token.EQ) {
3149                 lexer.nextToken();
3150             }
3151             SQLExpr initialSize = this.exprParser.expr();
3152             stmt.setInitialSize(initialSize);
3153         }
3154 
3155         if (lexer.identifierEquals(FnvHash.Constants.WAIT)) {
3156             lexer.nextToken();
3157             stmt.setWait(true);
3158         }
3159 
3160         if (lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
3161             lexer.nextToken();
3162             if (lexer.token() == Token.EQ) {
3163                 lexer.nextToken();
3164             }
3165             SQLExpr engine = this.exprParser.expr();
3166             stmt.setEngine(engine);
3167         }
3168 
3169         return stmt;
3170     }
3171 
3172     protected SQLStatement parseAlterLogFileGroup() {
3173         if (lexer.token() == Token.ALTER) {
3174             lexer.nextToken();
3175         }
3176         acceptIdentifier("LOGFILE");
3177         accept(Token.GROUP);
3178 
3179         SQLName name = this.exprParser.name();
3180 
3181         MySqlAlterLogFileGroupStatement stmt = new MySqlAlterLogFileGroupStatement();
3182         stmt.setName(name);
3183 
3184         acceptIdentifier("ADD");
3185         acceptIdentifier("UNDOFILE");
3186 
3187         SQLExpr fileName = this.exprParser.primary();
3188         stmt.setAddUndoFile(fileName);
3189 
3190         if (lexer.identifierEquals(FnvHash.Constants.INITIAL_SIZE)) {
3191             lexer.nextToken();
3192             if (lexer.token() == Token.EQ) {
3193                 lexer.nextToken();
3194             }
3195             SQLExpr initialSize = this.exprParser.expr();
3196             stmt.setInitialSize(initialSize);
3197         }
3198 
3199         if (lexer.identifierEquals(FnvHash.Constants.WAIT)) {
3200             lexer.nextToken();
3201             stmt.setWait(true);
3202         }
3203 
3204         if (lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
3205             lexer.nextToken();
3206             if (lexer.token() == Token.EQ) {
3207                 lexer.nextToken();
3208             }
3209             SQLExpr engine = this.exprParser.expr();
3210             stmt.setEngine(engine);
3211         }
3212 
3213         return stmt;
3214     }
3215 
3216     protected SQLStatement parseAlterProcedure() {
3217         if (lexer.token() == Token.ALTER) {
3218             lexer.nextToken();
3219         }
3220         accept(Token.PROCEDURE);
3221 
3222         SQLAlterProcedureStatement stmt = new SQLAlterProcedureStatement();
3223         stmt.setDbType(dbType);
3224 
3225         SQLName name = this.exprParser.name();
3226         stmt.setName(name);
3227 
3228         // for mysql
3229         for (;;) {
3230             if (lexer.token() == Token.COMMENT) {
3231                 lexer.nextToken();
3232                 SQLExpr comment = this.exprParser.primary();
3233                 stmt.setComment(comment);
3234             } else if (lexer.identifierEquals(FnvHash.Constants.LANGUAGE)) {
3235                 lexer.nextToken();
3236                 acceptIdentifier("SQL");
3237                 stmt.setLanguageSql(true);
3238             } else if (lexer.identifierEquals(FnvHash.Constants.SQL)) {
3239                 lexer.nextToken();
3240                 acceptIdentifier("SECURITY");
3241 
3242                 SQLExpr sqlSecurity = this.exprParser.name();
3243                 stmt.setSqlSecurity(sqlSecurity);
3244             } else if (lexer.identifierEquals(FnvHash.Constants.CONTAINS) || lexer.token() == Token.CONTAINS) {
3245                 lexer.nextToken();
3246                 acceptIdentifier("SQL");
3247                 stmt.setContainsSql(true);
3248             } else {
3249                 break;
3250             }
3251         }
3252 
3253         return stmt;
3254     }
3255 
3256     override protected SQLStatement parseAlterFunction() {
3257         if (lexer.token() == Token.ALTER) {
3258             lexer.nextToken();
3259         }
3260         accept(Token.FUNCTION);
3261 
3262         SQLAlterFunctionStatement stmt = new SQLAlterFunctionStatement();
3263         stmt.setDbType(dbType);
3264 
3265         SQLName name = this.exprParser.name();
3266         stmt.setName(name);
3267 
3268         // for mysql
3269         for (;;) {
3270             if (lexer.token() == Token.COMMENT) {
3271                 lexer.nextToken();
3272                 SQLExpr comment = this.exprParser.primary();
3273                 stmt.setComment(comment);
3274             } else if (lexer.identifierEquals(FnvHash.Constants.LANGUAGE)) {
3275                 lexer.nextToken();
3276                 acceptIdentifier("SQL");
3277                 stmt.setLanguageSql(true);
3278             } else if (lexer.identifierEquals(FnvHash.Constants.SQL)) {
3279                 lexer.nextToken();
3280                 acceptIdentifier("SECURITY");
3281 
3282                 SQLExpr sqlSecurity = this.exprParser.name();
3283                 stmt.setSqlSecurity(sqlSecurity);
3284             } else if (lexer.identifierEquals(FnvHash.Constants.CONTAINS) || lexer.token() == Token.CONTAINS) {
3285                 lexer.nextToken();
3286                 acceptIdentifier("SQL");
3287                 stmt.setContainsSql(true);
3288             } else {
3289                 break;
3290             }
3291         }
3292 
3293         return stmt;
3294     }
3295 
3296     protected SQLStatement parseCreateEvent() {
3297         if (lexer.token() == Token.CREATE) {
3298             lexer.nextToken();
3299         }
3300 
3301         MySqlCreateEventStatement stmt = new MySqlCreateEventStatement();
3302 
3303         if (lexer.identifierEquals(FnvHash.Constants.DEFINER)) {
3304             lexer.nextToken();
3305             accept(Token.EQ);
3306             SQLName definer = this.getExprParser().userName();
3307             stmt.setDefiner(definer);
3308 
3309             if (lexer.token() == Token.LPAREN) {
3310                 lexer.nextToken();
3311                 accept(Token.RPAREN);
3312             }
3313         }
3314 
3315         acceptIdentifier("EVENT");
3316 
3317         if (lexer.token() == Token.IF) {
3318             lexer.nextToken();
3319             accept(Token.NOT);
3320             accept(Token.EXISTS);
3321             stmt.setIfNotExists(true);
3322         }
3323 
3324         SQLName eventName = this.exprParser.name();
3325         stmt.setName(eventName);
3326 
3327         while (lexer.token() == Token.ON) {
3328             lexer.nextToken();
3329             if (lexer.identifierEquals(FnvHash.Constants.SCHEDULE)) {
3330                 lexer.nextToken();
3331                 MySqlEventSchedule schedule = parseSchedule();
3332                 stmt.setSchedule(schedule);
3333             } else if (lexer.identifierEquals(FnvHash.Constants.COMPLETION)) {
3334                 lexer.nextToken();
3335 
3336                 bool value;
3337                 if (lexer.token() == Token.NOT) {
3338                     lexer.nextToken();
3339                     value = false;
3340                 } else {
3341                     value = true;
3342                 }
3343                 acceptIdentifier("PRESERVE");
3344                 stmt.setOnCompletionPreserve(value);
3345             } else {
3346                 throw new ParserException("TODO " ~ lexer.info());
3347             }
3348         }
3349 
3350         if (lexer.identifierEquals(FnvHash.Constants.RENAME)) {
3351             lexer.nextToken();
3352             accept(Token.TO);
3353             SQLName renameTo = this.exprParser.name();
3354             stmt.setRenameTo(renameTo);
3355         }
3356 
3357         if (lexer.token() == Token.ENABLE) {
3358             stmt.setEnable(new Boolean(true));
3359             lexer.nextToken();
3360         } else if (lexer.token() == Token.DISABLE) {
3361             lexer.nextToken();
3362             stmt.setEnable(new Boolean(false));
3363 
3364             if (lexer.token() == Token.ON) {
3365                 lexer.nextToken();
3366                 acceptIdentifier("SLAVE");
3367                 stmt.setDisableOnSlave(true);
3368             }
3369         }
3370 
3371         if (lexer.token() == Token.COMMENT) {
3372             lexer.nextToken();
3373             SQLExpr comment = this.exprParser.primary();
3374             stmt.setComment(comment);
3375         }
3376 
3377         if (lexer.token() == Token.DO) {
3378             lexer.nextToken();
3379             SQLStatement eventBody = this.parseStatement();
3380             stmt.setEventBody(eventBody);
3381         } else if (lexer.token() == Token.IDENTIFIER) {
3382             SQLExpr expr = this.exprParser.expr();
3383             SQLExprStatement eventBody = new SQLExprStatement(expr);
3384             eventBody.setDbType(dbType);
3385             stmt.setEventBody(eventBody);
3386         }
3387 
3388         return stmt;
3389     }
3390 
3391     protected SQLStatement parseAlterEvent() {
3392         if (lexer.token() == Token.ALTER) {
3393             lexer.nextToken();
3394         }
3395 
3396         MySqlAlterEventStatement stmt = new MySqlAlterEventStatement();
3397 
3398         if (lexer.identifierEquals(FnvHash.Constants.DEFINER)) {
3399             lexer.nextToken();
3400             accept(Token.EQ);
3401             SQLName definer = this.getExprParser().userName();
3402             stmt.setDefiner(definer);
3403         }
3404 
3405         acceptIdentifier("EVENT");
3406 
3407         SQLName eventName = this.exprParser.name();
3408         stmt.setName(eventName);
3409 
3410         while (lexer.token() == Token.ON) {
3411             lexer.nextToken();
3412             if (lexer.identifierEquals(FnvHash.Constants.SCHEDULE)) {
3413                 lexer.nextToken();
3414                 MySqlEventSchedule schedule = parseSchedule();
3415                 stmt.setSchedule(schedule);
3416             } else if (lexer.identifierEquals(FnvHash.Constants.COMPLETION)) {
3417                 lexer.nextToken();
3418 
3419                 bool value;
3420                 if (lexer.token() == Token.NOT) {
3421                     lexer.nextToken();
3422                     value = false;
3423                 } else {
3424                     value = true;
3425                 }
3426                 acceptIdentifier("PRESERVE");
3427                 stmt.setOnCompletionPreserve(value);
3428             } else {
3429                 throw new ParserException("TODO " ~ lexer.info());
3430             }
3431         }
3432 
3433         if (lexer.identifierEquals(FnvHash.Constants.RENAME)) {
3434             lexer.nextToken();
3435             accept(Token.TO);
3436             SQLName renameTo = this.exprParser.name();
3437             stmt.setRenameTo(renameTo);
3438         }
3439 
3440         if (lexer.token() == Token.ENABLE) {
3441             stmt.setEnable(new Boolean(true));
3442             lexer.nextToken();
3443         } else if (lexer.token() == Token.DISABLE) {
3444             lexer.nextToken();
3445             stmt.setEnable(new Boolean(false));
3446 
3447             if (lexer.token() == Token.ON) {
3448                 lexer.nextToken();
3449                 acceptIdentifier("SLAVE");
3450                 stmt.setDisableOnSlave(true);
3451             }
3452         }
3453 
3454         if (lexer.token() == Token.COMMENT) {
3455             lexer.nextToken();
3456             SQLExpr comment = this.exprParser.primary();
3457             stmt.setComment(comment);
3458         }
3459 
3460         if (lexer.token() == Token.DO) {
3461             lexer.nextToken();
3462             SQLStatement eventBody = this.parseStatement();
3463             stmt.setEventBody(eventBody);
3464         } else if (lexer.token() == Token.IDENTIFIER) {
3465             SQLExpr expr = this.exprParser.expr();
3466             SQLExprStatement eventBody = new SQLExprStatement(expr);
3467             eventBody.setDbType(dbType);
3468             stmt.setEventBody(eventBody);
3469         }
3470 
3471         return stmt;
3472     }
3473 
3474     private MySqlEventSchedule parseSchedule() {
3475         MySqlEventSchedule schedule = new MySqlEventSchedule();
3476 
3477         if (lexer.identifierEquals(FnvHash.Constants.AT)) {
3478             lexer.nextToken();
3479             schedule.setAt(this.exprParser.expr());
3480         } else if (lexer.identifierEquals(FnvHash.Constants.EVERY)) {
3481             lexer.nextToken();
3482             SQLExpr value = this.exprParser.expr();
3483             string unit = lexer.stringVal();
3484             lexer.nextToken();
3485 
3486             SQLIntervalExpr intervalExpr = new SQLIntervalExpr();
3487             intervalExpr.setValue(value);
3488             intervalExpr.setUnit(SQLIntervalUnit(toUpper(unit)));
3489 
3490             schedule.setEvery(intervalExpr);
3491         }
3492 
3493         if (lexer.identifierEquals(FnvHash.Constants.STARTS)) {
3494             lexer.nextToken();
3495             schedule.setStarts(this.exprParser.expr());
3496 
3497             if (lexer.identifierEquals(FnvHash.Constants.ENDS)) {
3498                 lexer.nextToken();
3499                 schedule.setEnds(this.exprParser.expr());
3500             }
3501         } else if (lexer.identifierEquals(FnvHash.Constants.ENDS)) {
3502             lexer.nextToken();
3503             schedule.setEnds(this.exprParser.expr());
3504         }
3505 
3506         return schedule;
3507     }
3508 
3509     protected SQLStatement parseAlterTable(bool ignore) {
3510         lexer.nextToken();
3511 
3512         SQLAlterTableStatement stmt = new SQLAlterTableStatement(getDbType());
3513         stmt.setIgnore(ignore);
3514         stmt.setName(this.exprParser.name());
3515 
3516         for (; ; ) {
3517             if (lexer.token() == Token.DROP) {
3518                 parseAlterDrop(stmt);
3519             } else if (lexer.token() == Token.TRUNCATE) {
3520                 lexer.nextToken();
3521                 accept(Token.PARTITION);
3522 
3523                 SQLAlterTableTruncatePartition item = new SQLAlterTableTruncatePartition();
3524                 if (lexer.token() == Token.ALL) {
3525                     item.getPartitions().add(new SQLIdentifierExpr("ALL"));
3526                     lexer.nextToken();
3527                 } else {
3528                     this.exprParser.names(item.getPartitions(), item);
3529                 }
3530                 stmt.addItem(item);
3531             } else if (lexer.identifierEquals("ADD")) {
3532                 lexer.nextToken();
3533 
3534                 if (lexer.token() == Token.COLUMN) {
3535                     lexer.nextToken();
3536                     parseAlterTableAddColumn(stmt);
3537                 } else if (lexer.token() == Token.INDEX
3538                         || lexer.token() == Token.FULLTEXT
3539                         || lexer.identifierEquals(FnvHash.Constants.SPATIAL)) {
3540                     SQLAlterTableAddIndex item = parseAlterTableAddIndex();
3541                     item.setParent(stmt);
3542                     stmt.addItem(item);
3543                 } else if (lexer.token() == Token.UNIQUE) {
3544                     SQLAlterTableAddIndex item = parseAlterTableAddIndex();
3545                     item.setParent(stmt);
3546                     stmt.addItem(item);
3547                 } else if (lexer.token() == Token.PRIMARY) {
3548                     SQLPrimaryKey primaryKey = this.exprParser.parsePrimaryKey();
3549                     SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(primaryKey);
3550                     stmt.addItem(item);
3551                 } else if (lexer.token() == Token.KEY) {
3552                     // throw new ParserException("TODO " ~ lexer.token() +
3553                     // " " ~ lexer.stringVal());
3554                     SQLAlterTableAddIndex item = parseAlterTableAddIndex();
3555                     item.setParent(stmt);
3556                     stmt.addItem(item);
3557                 } else if (lexer.token() == Token.FOREIGN) {
3558                     MysqlForeignKey fk = this.getExprParser().parseForeignKey();
3559                     SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(fk);
3560 
3561                     stmt.addItem(item);
3562                 } else if (lexer.token() == Token.CONSTRAINT) {
3563                     lexer.nextToken();
3564 
3565                     if (lexer.token() == Token.PRIMARY) {
3566                         SQLPrimaryKey primaryKey = (cast(MySqlExprParser) this.exprParser).parsePrimaryKey();
3567                         SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(primaryKey);
3568                         item.setParent(stmt);
3569 
3570                         stmt.addItem(item);
3571                     } else if (lexer.token() == Token.FOREIGN) {
3572                         MysqlForeignKey fk = this.getExprParser().parseForeignKey();
3573                         fk.setHasConstraint(true);
3574 
3575                         SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(fk);
3576 
3577                         stmt.addItem(item);
3578                     } else if (lexer.token() == Token.UNIQUE) {
3579 
3580                         SQLUnique unique = this.exprParser.parseUnique();
3581                         SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(unique);
3582                         stmt.addItem(item);
3583                     } else {
3584                         SQLName constraintName = this.exprParser.name();
3585 
3586                         if (lexer.token() == Token.PRIMARY) {
3587                             SQLPrimaryKey primaryKey = (cast(MySqlExprParser) this.exprParser).parsePrimaryKey();
3588 
3589                             primaryKey.setName(constraintName);
3590 
3591                             SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(primaryKey);
3592                             item.setParent(stmt);
3593 
3594                             stmt.addItem(item);
3595                         } else if (lexer.token() == Token.FOREIGN) {
3596                             MysqlForeignKey fk = this.getExprParser().parseForeignKey();
3597                             fk.setName(constraintName);
3598                             fk.setHasConstraint(true);
3599 
3600                             SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(fk);
3601 
3602                             stmt.addItem(item);
3603                         } else if (lexer.token() == Token.UNIQUE) {
3604                             SQLUnique unique = this.exprParser.parseUnique();
3605                             SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(unique);
3606                             stmt.addItem(item);
3607                         } else {
3608                             throw new ParserException("TODO " ~ lexer.info());
3609                         }
3610                     }
3611                 } else if (lexer.token() == Token.PARTITION) {
3612                     lexer.nextToken();
3613 
3614                     SQLAlterTableAddPartition item = new SQLAlterTableAddPartition();
3615 
3616                     if (lexer.identifierEquals("PARTITIONS")) {
3617                         lexer.nextToken();
3618                         item.setPartitionCount(this.exprParser.integerExpr());
3619                     }
3620 
3621                     if (lexer.token() == Token.LPAREN) {
3622                         lexer.nextToken();
3623                         SQLPartition partition = this.getExprParser().parsePartition();
3624                         accept(Token.RPAREN);
3625                         item.addPartition(partition);
3626                     }
3627 
3628                     stmt.addItem(item);
3629                 } else {
3630                     parseAlterTableAddColumn(stmt);
3631                 }
3632             } else if (lexer.token() == Token.ALTER) {
3633                 lexer.nextToken();
3634                 if (lexer.token() == Token.COLUMN) {
3635                     lexer.nextToken();
3636                 }
3637 
3638                 MySqlAlterTableAlterColumn alterColumn = new MySqlAlterTableAlterColumn();
3639                 alterColumn.setColumn(this.exprParser.name());
3640 
3641                 if (lexer.token() == Token.SET) {
3642                     lexer.nextToken();
3643                     accept(Token.DEFAULT);
3644 
3645                     alterColumn.setDefaultExpr(this.exprParser.expr());
3646                 } else {
3647                     accept(Token.DROP);
3648                     accept(Token.DEFAULT);
3649                     alterColumn.setDropDefault(true);
3650                 }
3651 
3652                 stmt.addItem(alterColumn);
3653             } else if (lexer.identifierEquals("CHANGE")) {
3654                 lexer.nextToken();
3655                 if (lexer.token() == Token.COLUMN) {
3656                     lexer.nextToken();
3657                 }
3658                 MySqlAlterTableChangeColumn item = new MySqlAlterTableChangeColumn();
3659                 item.setColumnName(this.exprParser.name());
3660                 item.setNewColumnDefinition(this.exprParser.parseColumn());
3661                 if (lexer.identifierEquals("AFTER")) {
3662                     lexer.nextToken();
3663                     item.setAfterColumn(this.exprParser.name());
3664                 } else if (lexer.identifierEquals("FIRST")) {
3665                     lexer.nextToken();
3666                     if (lexer.token() == Token.IDENTIFIER) {
3667                         item.setFirstColumn(this.exprParser.name());
3668                     } else {
3669                         item.setFirst(true);
3670                     }
3671                 }
3672                 stmt.addItem(item);
3673             } else if (lexer.identifierEquals("MODIFY")) {
3674                 lexer.nextToken();
3675 
3676                 if (lexer.token() == Token.COLUMN) {
3677                     lexer.nextToken();
3678                 }
3679 
3680                 bool paren = false;
3681                 if (lexer.token() == Token.LPAREN) {
3682                     paren = true;
3683                     lexer.nextToken();
3684                 }
3685 
3686                 for (; ; ) {
3687                     MySqlAlterTableModifyColumn item = new MySqlAlterTableModifyColumn();
3688                     item.setNewColumnDefinition(this.exprParser.parseColumn());
3689                     if (lexer.identifierEquals("AFTER")) {
3690                         lexer.nextToken();
3691                         item.setAfterColumn(this.exprParser.name());
3692                     } else if (lexer.identifierEquals("FIRST")) {
3693                         lexer.nextToken();
3694                         if (lexer.token() == Token.IDENTIFIER) {
3695                             item.setFirstColumn(this.exprParser.name());
3696                         } else {
3697                             item.setFirst(true);
3698                         }
3699                     }
3700                     stmt.addItem(item);
3701 
3702                     if (paren && lexer.token() == Token.COMMA) {
3703                         lexer.nextToken();
3704                         continue;
3705                     }
3706                     break;
3707                 }
3708 
3709                 if (paren) {
3710                     accept(Token.RPAREN);
3711                 }
3712             } else if (lexer.token() == Token.DISABLE) {
3713                 lexer.nextToken();
3714 
3715                 if (lexer.token() == Token.CONSTRAINT) {
3716                     lexer.nextToken();
3717                     SQLAlterTableDisableConstraint item = new SQLAlterTableDisableConstraint();
3718                     item.setConstraintName(this.exprParser.name());
3719                     stmt.addItem(item);
3720                 } else {
3721                     acceptIdentifier("KEYS");
3722                     SQLAlterTableDisableKeys item = new SQLAlterTableDisableKeys();
3723                     stmt.addItem(item);
3724                 }
3725             } else if (lexer.token() == Token.ENABLE) {
3726                 lexer.nextToken();
3727                 if (lexer.token() == Token.CONSTRAINT) {
3728                     lexer.nextToken();
3729                     SQLAlterTableEnableConstraint item = new SQLAlterTableEnableConstraint();
3730                     item.setConstraintName(this.exprParser.name());
3731                     stmt.addItem(item);
3732                 } else {
3733                     acceptIdentifier("KEYS");
3734                     SQLAlterTableEnableKeys item = new SQLAlterTableEnableKeys();
3735                     stmt.addItem(item);
3736                 }
3737             } else if (lexer.identifierEquals("RENAME")) {
3738                 lexer.nextToken();
3739 
3740                 if (lexer.token() == Token.INDEX) {
3741                     lexer.nextToken();
3742                     SQLName name = this.exprParser.name();
3743                     accept(Token.TO);
3744                     SQLName to = this.exprParser.name();
3745                     SQLAlterTableRenameIndex item = new SQLAlterTableRenameIndex(name, to);
3746                     stmt.addItem(item);
3747                     continue;
3748                 }
3749 
3750                 if (lexer.token() == Token.TO || lexer.token() == Token.AS) {
3751                     lexer.nextToken();
3752                 }
3753 
3754                 if (stmt.getItems().size() > 0) {
3755                     SQLAlterTableRename item = new SQLAlterTableRename();
3756                     SQLName to = this.exprParser.name();
3757                     item.setTo(to);
3758                     stmt.addItem(item);
3759                 } else {
3760                     MySqlRenameTableStatement renameStmt = new MySqlRenameTableStatement();
3761                     MySqlRenameTableStatement.Item item = new MySqlRenameTableStatement.Item();
3762                     item.setName(cast(SQLName) stmt.getTableSource().getExpr());
3763                     item.setTo(this.exprParser.name());
3764                     // SQLAlterTableRename
3765                     renameStmt.addItem(item);
3766 
3767                     return renameStmt;
3768                 }   
3769             } else if (lexer.token() == Token.ORDER) {
3770                 throw new ParserException("TODO " ~ lexer.info());
3771             } else if (lexer.identifierEquals("CONVERT")) {
3772                 lexer.nextToken();
3773                 accept(Token.TO);
3774                 acceptIdentifier("CHARACTER");
3775                 accept(Token.SET);
3776 
3777                 SQLAlterTableConvertCharSet item = new SQLAlterTableConvertCharSet();
3778                 SQLExpr charset = this.exprParser.primary();
3779                 item.setCharset(charset);
3780 
3781                 if (lexer.identifierEquals("COLLATE")) {
3782                     lexer.nextToken();
3783                     SQLExpr collate = this.exprParser.primary();
3784                     item.setCollate(collate);
3785                 }
3786 
3787                 stmt.addItem(item);
3788             } else if (lexer.token() == Token.DEFAULT) {
3789                 lexer.nextToken();
3790                 if (lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
3791                     SQLAlterCharacter item = alterTableCharacter();
3792                     stmt.addItem(item);
3793                 } else {
3794                     throw new ParserException("TODO " ~ lexer.info());
3795                 }
3796             } else if (lexer.identifierEquals("DISCARD")) {
3797                 lexer.nextToken();
3798 
3799                 if (lexer.token() == Token.PARTITION) {
3800                     lexer.nextToken();
3801                     SQLAlterTableDiscardPartition item = new SQLAlterTableDiscardPartition();
3802 
3803                     if (lexer.token() == Token.ALL) {
3804                         lexer.nextToken();
3805                         item.getPartitions().add(new SQLIdentifierExpr("ALL"));
3806                     } else {
3807                         this.exprParser.names(item.getPartitions(), item);
3808                     }
3809 
3810                     if (lexer.token() == Token.TABLESPACE) {
3811                         lexer.nextToken();
3812                         item.setTablespace(true);
3813                     }
3814 
3815                     stmt.addItem(item);
3816                 } else {
3817                     accept(Token.TABLESPACE);
3818                     MySqlAlterTableDiscardTablespace item = new MySqlAlterTableDiscardTablespace();
3819                     stmt.addItem(item);
3820                 }
3821             } else if (lexer.token() == Token.CHECK) {
3822                 lexer.nextToken();
3823                 accept(Token.PARTITION);
3824 
3825                 SQLAlterTableCheckPartition item = new SQLAlterTableCheckPartition();
3826 
3827                 if (lexer.token() == Token.ALL) {
3828                     lexer.nextToken();
3829                     item.getPartitions().add(new SQLIdentifierExpr("ALL"));
3830                 } else {
3831                     this.exprParser.names(item.getPartitions(), item);
3832                 }
3833 
3834                 stmt.addItem(item);
3835 
3836             } else if (lexer.identifierEquals("IMPORT")) {
3837                 lexer.nextToken();
3838 
3839                 if (lexer.token() == Token.PARTITION) {
3840                     lexer.nextToken();
3841                     SQLAlterTableImportPartition item = new SQLAlterTableImportPartition();
3842 
3843                     if (lexer.token() == Token.ALL) {
3844                         lexer.nextToken();
3845                         item.getPartitions().add(new SQLIdentifierExpr("ALL"));
3846                     } else {
3847                         this.exprParser.names(item.getPartitions(), item);
3848                     }
3849 
3850                     stmt.addItem(item);
3851                 } else {
3852                     accept(Token.TABLESPACE);
3853                     MySqlAlterTableImportTablespace item = new MySqlAlterTableImportTablespace();
3854                     stmt.addItem(item);
3855                 }
3856             } else if (lexer.token() == Token.ANALYZE) {
3857                 lexer.nextToken();
3858                 accept(Token.PARTITION);
3859 
3860                 SQLAlterTableAnalyzePartition item = new SQLAlterTableAnalyzePartition();
3861 
3862                 if (lexer.token() == Token.ALL) {
3863                     lexer.nextToken();
3864                     item.getPartitions().add(new SQLIdentifierExpr("ALL"));
3865                 } else {
3866                     this.exprParser.names(item.getPartitions(), item);
3867                 }
3868                 stmt.addItem(item);
3869             } else if (lexer.identifierEquals("FORCE")) {
3870                 throw new ParserException("TODO " ~ lexer.info());
3871             } else if (lexer.identifierEquals("COALESCE")) {
3872                 lexer.nextToken();
3873                 accept(Token.PARTITION);
3874 
3875                 SQLAlterTableCoalescePartition item = new SQLAlterTableCoalescePartition();
3876                 SQLIntegerExpr countExpr = this.exprParser.integerExpr();
3877                 item.setCount(countExpr);
3878                 stmt.addItem(item);
3879             } else if (lexer.identifierEquals("REORGANIZE")) {
3880                 lexer.nextToken();
3881                 accept(Token.PARTITION);
3882 
3883                 SQLAlterTableReOrganizePartition item = new SQLAlterTableReOrganizePartition();
3884 
3885                 this.exprParser.names(item.getNames(), item);
3886 
3887                 accept(Token.INTO);
3888                 accept(Token.LPAREN);
3889                 for (; ; ) {
3890                     SQLPartition partition = this.getExprParser().parsePartition();
3891 
3892                     item.addPartition(partition);
3893 
3894                     if (lexer.token() == Token.COMMA) {
3895                         lexer.nextToken();
3896                         continue;
3897                     } else {
3898                         break;
3899                     }
3900                 }
3901                 accept(Token.RPAREN);
3902                 stmt.addItem(item);
3903             } else if (lexer.identifierEquals("EXCHANGE")) {
3904                 lexer.nextToken();
3905                 accept(Token.PARTITION);
3906 
3907                 SQLAlterTableExchangePartition item = new SQLAlterTableExchangePartition();
3908 
3909                 SQLName partition = this.exprParser.name();
3910                 item.setPartition(partition);
3911 
3912                 accept(Token.WITH);
3913                 accept(Token.TABLE);
3914                 SQLName table = this.exprParser.name();
3915                 item.setTable(table);
3916 
3917                 if (lexer.token() == Token.WITH) {
3918                     lexer.nextToken();
3919                     acceptIdentifier("VALIDATION");
3920                     item.setValidation(true);
3921                 } else if (lexer.identifierEquals(FnvHash.Constants.WITHOUT)) {
3922                     lexer.nextToken();
3923                     acceptIdentifier("VALIDATION");
3924                     item.setValidation(false);
3925                 }
3926 
3927 
3928                 stmt.addItem(item);
3929             } else if (lexer.token() == Token.OPTIMIZE) {
3930                 lexer.nextToken();
3931 
3932                 accept(Token.PARTITION);
3933 
3934                 SQLAlterTableOptimizePartition item = new SQLAlterTableOptimizePartition();
3935 
3936                 if (lexer.token() == Token.ALL) {
3937                     lexer.nextToken();
3938                     item.getPartitions().add(new SQLIdentifierExpr("ALL"));
3939                 } else {
3940                     this.exprParser.names(item.getPartitions(), item);
3941                 }
3942 
3943                 stmt.addItem(item);
3944             } else if (lexer.identifierEquals("REBUILD")) {
3945                 lexer.nextToken();
3946 
3947                 accept(Token.PARTITION);
3948 
3949                 SQLAlterTableRebuildPartition item = new SQLAlterTableRebuildPartition();
3950 
3951                 if (lexer.token() == Token.ALL) {
3952                     lexer.nextToken();
3953                     item.getPartitions().add(new SQLIdentifierExpr("ALL"));
3954                 } else {
3955                     this.exprParser.names(item.getPartitions(), item);
3956                 }
3957 
3958                 stmt.addItem(item);
3959             } else if (lexer.identifierEquals("REPAIR")) {
3960                 lexer.nextToken();
3961 
3962                 accept(Token.PARTITION);
3963 
3964                 SQLAlterTableRepairPartition item = new SQLAlterTableRepairPartition();
3965 
3966                 if (lexer.token() == Token.ALL) {
3967                     lexer.nextToken();
3968                     item.getPartitions().add(new SQLIdentifierExpr("ALL"));
3969                 } else {
3970                     this.exprParser.names(item.getPartitions(), item);
3971                 }
3972 
3973                 stmt.addItem(item);
3974             } else if (lexer.identifierEquals("REMOVE")) {
3975                 lexer.nextToken();
3976                 acceptIdentifier("PARTITIONING");
3977                 stmt.setRemovePatiting(true);
3978             } else if (lexer.identifierEquals("UPGRADE")) {
3979                 lexer.nextToken();
3980                 acceptIdentifier("PARTITIONING");
3981                 stmt.setUpgradePatiting(true);
3982             } else if (lexer.identifierEquals("ALGORITHM")) {
3983                 lexer.nextToken();
3984                 accept(Token.EQ);
3985                 stmt.addItem(new MySqlAlterTableOption("ALGORITHM", lexer.stringVal()));
3986                 lexer.nextToken();
3987             } else if (lexer.identifierEquals(ENGINE)) {
3988                 lexer.nextToken();
3989                 accept(Token.EQ);
3990                 stmt.addItem(new MySqlAlterTableOption(ENGINE, lexer.stringVal()));
3991                 lexer.nextToken();
3992             } else if (lexer.identifierEquals(AUTO_INCREMENT)) {
3993                 lexer.nextToken();
3994                 accept(Token.EQ);
3995                 stmt.addItem(new MySqlAlterTableOption(AUTO_INCREMENT, new SQLIntegerExpr(lexer.integerValue())));
3996                 lexer.nextToken();
3997             } else if (lexer.identifierEquals(COLLATE2)) {
3998                 lexer.nextToken();
3999                 accept(Token.EQ);
4000                 stmt.addItem(new MySqlAlterTableOption(COLLATE2, lexer.stringVal()));
4001                 lexer.nextToken();
4002             } else if (lexer.identifierEquals("PACK_KEYS")) {
4003                 lexer.nextToken();
4004                 accept(Token.EQ);
4005                 if (lexer.identifierEquals("PACK")) {
4006                     lexer.nextToken();
4007                     accept(Token.ALL);
4008                     stmt.addItem(new MySqlAlterTableOption("PACK_KEYS", "PACK ALL"));
4009                 } else {
4010                     stmt.addItem(new MySqlAlterTableOption("PACK_KEYS", lexer.stringVal()));
4011                     lexer.nextToken();
4012                 }
4013             } else if (lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
4014                 SQLAlterCharacter item = alterTableCharacter();
4015                 stmt.addItem(item);
4016             } else if (lexer.token() == Token.COMMENT) {
4017                 lexer.nextToken();
4018                 if (lexer.token() == Token.EQ) {
4019                     accept(Token.EQ);
4020                 }
4021                 stmt.addItem(new MySqlAlterTableOption("COMMENT", '\'' ~ lexer.stringVal() ~ '\''));
4022                 lexer.nextToken();
4023             } else if (lexer.token() == Token.UNION) {
4024                 lexer.nextToken();
4025                 if (lexer.token() == Token.EQ) {
4026                     lexer.nextToken();
4027                 }
4028 
4029                 accept(Token.LPAREN);
4030                 SQLTableSource tableSrc = this.createSQLSelectParser().parseTableSource();
4031                 stmt.getTableOptions().put("UNION", tableSrc);
4032                 accept(Token.RPAREN);
4033             } else if (lexer.identifierEquals("ROW_FORMAT")) {
4034                 lexer.nextToken();
4035                 if (lexer.token() == Token.EQ) {
4036                     lexer.nextToken();
4037                 }
4038 
4039                 if (lexer.token() == Token.DEFAULT || lexer.token() == Token.IDENTIFIER) {
4040                     SQLIdentifierExpr rowFormat = new SQLIdentifierExpr(lexer.stringVal());
4041                     lexer.nextToken();
4042                     stmt.getTableOptions().put("ROW_FORMAT", rowFormat);
4043                 } else {
4044                     throw new ParserException("illegal syntax. " ~ lexer.info());
4045                 }
4046 
4047             } else {
4048                 break;
4049             }
4050 
4051             if (lexer.token() == Token.COMMA) {
4052                 lexer.nextToken();
4053                 continue;
4054             } else {
4055                 break;
4056             }
4057         }
4058 
4059         return stmt;
4060     }
4061 
4062     private SQLAlterCharacter alterTableCharacter() {
4063         lexer.nextToken();
4064         accept(Token.SET);
4065         accept(Token.EQ);
4066         SQLAlterCharacter item = new SQLAlterCharacter();
4067         item.setCharacterSet(this.exprParser.primary());
4068         if (lexer.token() == Token.COMMA) {
4069             lexer.nextToken();
4070             acceptIdentifier(COLLATE2);
4071             accept(Token.EQ);
4072             item.setCollate(this.exprParser.primary());
4073         }
4074         return item;
4075     }
4076 
4077     protected void parseAlterTableAddColumn(SQLAlterTableStatement stmt) {
4078         bool parenFlag = false;
4079         if (lexer.token() == Token.LPAREN) {
4080             lexer.nextToken();
4081             parenFlag = true;
4082         }
4083 
4084         SQLAlterTableAddColumn item = new SQLAlterTableAddColumn();
4085         for (; ; ) {
4086 
4087             SQLColumnDefinition columnDef = this.exprParser.parseColumn();
4088             item.addColumn(columnDef);
4089             if (lexer.identifierEquals("AFTER")) {
4090                 lexer.nextToken();
4091                 item.setAfterColumn(this.exprParser.name());
4092             } else if (lexer.identifierEquals("FIRST")) {
4093                 lexer.nextToken();
4094                 if (lexer.token() == Token.IDENTIFIER) {
4095                     item.setFirstColumn(this.exprParser.name());
4096                 } else {
4097                     item.setFirst(true);
4098                 }
4099             }
4100 
4101             if (parenFlag && lexer.token() == Token.COMMA) {
4102                 lexer.nextToken();
4103                 continue;
4104             }
4105 
4106             break;
4107         }
4108 
4109         stmt.addItem(item);
4110 
4111         if (parenFlag) {
4112             accept(Token.RPAREN);
4113         }
4114     }
4115 
4116     override public void parseAlterDrop(SQLAlterTableStatement stmt) {
4117         lexer.nextToken();
4118         if (lexer.token() == Token.INDEX) {
4119             lexer.nextToken();
4120             SQLName indexName = this.exprParser.name();
4121             SQLAlterTableDropIndex item = new SQLAlterTableDropIndex();
4122             item.setIndexName(indexName);
4123             stmt.addItem(item);
4124         } else if (lexer.token() == Token.FOREIGN) {
4125             lexer.nextToken();
4126             accept(Token.KEY);
4127             SQLName indexName = this.exprParser.name();
4128             SQLAlterTableDropForeignKey item = new SQLAlterTableDropForeignKey();
4129             item.setIndexName(indexName);
4130             stmt.addItem(item);
4131         } else if (lexer.token() == Token.KEY) {
4132             lexer.nextToken();
4133             SQLName keyName = this.exprParser.name();
4134             SQLAlterTableDropKey item = new SQLAlterTableDropKey();
4135             item.setKeyName(keyName);
4136             stmt.addItem(item);
4137         } else if (lexer.token() == Token.PRIMARY) {
4138             lexer.nextToken();
4139             accept(Token.KEY);
4140             SQLAlterTableDropPrimaryKey item = new SQLAlterTableDropPrimaryKey();
4141             stmt.addItem(item);
4142         } else if (lexer.token() == Token.CONSTRAINT) {
4143             lexer.nextToken();
4144             SQLAlterTableDropConstraint item = new SQLAlterTableDropConstraint();
4145             item.setConstraintName(this.exprParser.name());
4146             stmt.addItem(item);
4147         } else if (lexer.token() == Token.COLUMN) {
4148             lexer.nextToken();
4149             SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
4150 
4151             SQLName name = exprParser.name();
4152             name.setParent(item);
4153             item.addColumn(name);
4154 
4155             while (lexer.token() == Token.COMMA) {
4156                 char markChar = lexer.current();
4157                 int markBp = lexer.bp();
4158 
4159                 lexer.nextToken();
4160                 if (lexer.identifierEquals("CHANGE")) {
4161                     lexer.reset(markBp, markChar, Token.COMMA);
4162                     break;
4163                 }
4164 
4165                 if (lexer.token() == Token.IDENTIFIER) {
4166                     if ("ADD".equalsIgnoreCase(lexer.stringVal())) {
4167                         lexer.reset(markBp, markChar, Token.COMMA);
4168                         break;
4169                     }
4170                     name = exprParser.name();
4171                     name.setParent(item);
4172                 } else {
4173                     lexer.reset(markBp, markChar, Token.COMMA);
4174                     break;
4175                 }
4176             }
4177 
4178             stmt.addItem(item);
4179         } else if (lexer.token() == Token.PARTITION) {
4180             SQLAlterTableDropPartition dropPartition = parseAlterTableDropPartition(false);
4181             stmt.addItem(dropPartition);
4182         } else if (lexer.token() == Token.IDENTIFIER) {
4183             SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
4184 
4185             SQLName name = this.exprParser.name();
4186             item.addColumn(name);
4187             stmt.addItem(item);
4188 
4189             if (lexer.token() == Token.COMMA) {
4190                 lexer.nextToken();
4191             }
4192 
4193             if (lexer.token() == Token.DROP) {
4194                 parseAlterDrop(stmt);
4195             }
4196         } else {
4197             super.parseAlterDrop(stmt);
4198         }
4199     }
4200 
4201     override public SQLStatement parseRename() {
4202         MySqlRenameTableStatement stmt = new MySqlRenameTableStatement();
4203 
4204         acceptIdentifier("RENAME");
4205 
4206         accept(Token.TABLE);
4207 
4208         for (; ; ) {
4209             MySqlRenameTableStatement.Item item = new MySqlRenameTableStatement.Item();
4210             item.setName(this.exprParser.name());
4211             accept(Token.TO);
4212             item.setTo(this.exprParser.name());
4213 
4214             stmt.addItem(item);
4215 
4216             if (lexer.token() == Token.COMMA) {
4217                 lexer.nextToken();
4218                 continue;
4219             }
4220 
4221             break;
4222         }
4223 
4224         return stmt;
4225     }
4226 
4227     override public SQLStatement parseCreateDatabase() {
4228         if (lexer.token() == Token.CREATE) {
4229             lexer.nextToken();
4230         }
4231 
4232         if (lexer.token() == Token.SCHEMA) {
4233             lexer.nextToken();
4234         } else {
4235             accept(Token.DATABASE);
4236         }
4237 
4238         SQLCreateDatabaseStatement stmt = new SQLCreateDatabaseStatement(DBType.MYSQL.name);
4239 
4240         if (lexer.token() == Token.IF) {
4241             lexer.nextToken();
4242             accept(Token.NOT);
4243             accept(Token.EXISTS);
4244             stmt.setIfNotExists(true);
4245         }
4246 
4247         stmt.setName(this.exprParser.name());
4248 
4249         if (lexer.token() == Token.DEFAULT) {
4250             lexer.nextToken();
4251         }
4252 
4253         if (lexer.token() == Token.HINT) {
4254             stmt.setHints(this.exprParser.parseHints());
4255         }
4256 
4257         if (lexer.token() == Token.DEFAULT) {
4258             lexer.nextToken();
4259         }
4260 
4261         for (;;) {
4262             if (lexer.identifierEquals("CHARACTER")) {
4263                 lexer.nextToken();
4264                 accept(Token.SET);
4265                 if (lexer.token() == Token.EQ) {
4266                     lexer.nextToken();
4267                 }
4268                 string charset = lexer.stringVal();
4269                 accept(Token.IDENTIFIER);
4270                 stmt.setCharacterSet(charset);
4271             } else if (lexer.identifierEquals("CHARSET")) {
4272                 lexer.nextToken();
4273                 if (lexer.token() == Token.EQ) {
4274                     lexer.nextToken();
4275                 }
4276                 string charset = lexer.stringVal();
4277                 accept(Token.IDENTIFIER);
4278                 stmt.setCharacterSet(charset);
4279             } else if (lexer.token() == Token.DEFAULT) {
4280                 lexer.nextToken();
4281             } else if (lexer.identifierEquals("COLLATE")) {
4282                 lexer.nextToken();
4283                 if (lexer.token() == Token.EQ) {
4284                     lexer.nextToken();
4285                 }
4286                 string collate = lexer.stringVal();
4287                 accept(Token.IDENTIFIER);
4288                 stmt.setCollate(collate);
4289             } else {
4290                 break;
4291             }
4292 
4293 
4294 
4295 
4296         }
4297 
4298         return stmt;
4299     }
4300 
4301     override protected void parseUpdateSet(SQLUpdateStatement update) {
4302         accept(Token.SET);
4303 
4304         for (; ; ) {
4305             SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem();
4306             update.addItem(item);
4307 
4308             if (lexer.token() != Token.COMMA) {
4309                 break;
4310             }
4311 
4312             lexer.nextToken();
4313         }
4314     }
4315 
4316     public SQLStatement parseAlterDatabase() {
4317         if (lexer.token() == Token.SCHEMA) {
4318             lexer.nextToken();
4319         } else {
4320             accept(Token.DATABASE);
4321         }
4322 
4323         SQLAlterDatabaseStatement stmt = new SQLAlterDatabaseStatement(dbType);
4324 
4325         SQLName name = this.exprParser.name();
4326         stmt.setName(name);
4327 
4328         if (lexer.identifierEquals("UPGRADE")) {
4329             lexer.nextToken();
4330             acceptIdentifier("DATA");
4331             acceptIdentifier("DIRECTORY");
4332             acceptIdentifier("NAME");
4333             stmt.setUpgradeDataDirectoryName(true);
4334         }
4335 
4336         if (lexer.token() == Token.DEFAULT) {
4337             lexer.nextToken();
4338             if (lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
4339                 SQLAlterCharacter item = alterTableCharacter();
4340                 stmt.setCharacter(item);
4341             } else {
4342                 throw new ParserException("TODO " ~ lexer.info());
4343             }
4344         } else if (lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
4345             SQLAlterCharacter item = alterTableCharacter();
4346             stmt.setCharacter(item);
4347         }
4348 
4349         return stmt;
4350     }
4351 
4352     public MySqlAlterUserStatement parseAlterUser() {
4353         accept(Token.USER);
4354 
4355         MySqlAlterUserStatement stmt = new MySqlAlterUserStatement();
4356         for (; ; ) {
4357             SQLExpr user = this.exprParser.expr();
4358             acceptIdentifier("PASSWORD");
4359             acceptIdentifier("EXPIRE");
4360             stmt.addUser(user);
4361 
4362             if (lexer.token() == Token.COMMA) {
4363                 lexer.nextToken();
4364                 continue;
4365             }
4366 
4367             break;
4368         }
4369         return stmt;
4370     }
4371 
4372     override public MySqlExprParser getExprParser() {
4373         return cast(MySqlExprParser) exprParser;
4374     }
4375 
4376 
4377     override public SQLCreateFunctionStatement parseCreateFunction() {
4378         SQLCreateFunctionStatement stmt = new SQLCreateFunctionStatement();
4379         stmt.setDbType(dbType);
4380 
4381         if (lexer.token() == Token.CREATE) {
4382             lexer.nextToken();
4383 
4384             if (lexer.token() == Token.OR) {
4385                 lexer.nextToken();
4386                 accept(Token.REPLACE);
4387                 stmt.setOrReplace(true);
4388             }
4389         }
4390 
4391         if (lexer.identifierEquals(FnvHash.Constants.DEFINER)) {
4392             lexer.nextToken();
4393             accept(Token.EQ);
4394             SQLName definer = this.getExprParser().userName();
4395             stmt.setDefiner(definer);
4396         }
4397 
4398         accept(Token.FUNCTION);
4399 
4400         stmt.setName(this.exprParser.name());
4401 
4402         if (lexer.token() == Token.LPAREN) {// match "("
4403             lexer.nextToken();
4404             parserParameters(stmt.getParameters(), stmt);
4405             accept(Token.RPAREN);// match ")"
4406         }
4407 
4408         acceptIdentifier("RETURNS");
4409         SQLDataType dataType = this.exprParser.parseDataType();
4410         stmt.setReturnDataType(dataType);
4411 
4412         for (;;) {
4413             if (lexer.identifierEquals("DETERMINISTIC")) {
4414                 lexer.nextToken();
4415                 stmt.setDeterministic(true);
4416                 continue;
4417             }
4418 
4419             break;
4420         }
4421 
4422         SQLStatement block;
4423         if (lexer.token() == Token.BEGIN) {
4424             block = this.parseBlock();
4425         } else {
4426             block = this.parseStatement();
4427         }
4428 
4429         stmt.setBlock(block);
4430 
4431         return stmt;
4432     }
4433     /**
4434      * parse create procedure statement
4435      */
4436     override public SQLCreateProcedureStatement parseCreateProcedure() {
4437         /**
4438          * CREATE OR REPALCE PROCEDURE SP_NAME(parameter_list) BEGIN block_statement END
4439          */
4440         SQLCreateProcedureStatement stmt = new SQLCreateProcedureStatement();
4441         stmt.setDbType(dbType);
4442 
4443         if (lexer.token() == Token.CREATE) {
4444             lexer.nextToken();
4445 
4446             if (lexer.token() == Token.OR) {
4447                 lexer.nextToken();
4448                 accept(Token.REPLACE);
4449                 stmt.setOrReplace(true);
4450             }
4451         }
4452 
4453         if (lexer.identifierEquals(FnvHash.Constants.DEFINER)) {
4454             lexer.nextToken();
4455             accept(Token.EQ);
4456             SQLName definer = this.getExprParser().userName();
4457             stmt.setDefiner(definer);
4458         }
4459 
4460         accept(Token.PROCEDURE);
4461 
4462         stmt.setName(this.exprParser.name());
4463 
4464         if (lexer.token() == Token.LPAREN) {// match "("
4465             lexer.nextToken();
4466             parserParameters(stmt.getParameters(), stmt);
4467             accept(Token.RPAREN);// match ")"
4468         }
4469 
4470         for (;;) {
4471             if (lexer.identifierEquals(FnvHash.Constants.DETERMINISTIC)) {
4472                 lexer.nextToken();
4473                 stmt.setDeterministic(true);
4474                 continue;
4475             }
4476             if (lexer.identifierEquals(FnvHash.Constants.CONTAINS) || lexer.token() == Token.CONTAINS) {
4477                 lexer.nextToken();
4478                 acceptIdentifier("SQL");
4479                 stmt.setContainsSql(true);
4480                 continue;
4481             }
4482 
4483             if (lexer.identifierEquals(FnvHash.Constants.SQL)) {
4484                 lexer.nextToken();
4485                 acceptIdentifier("SECURITY");
4486                 SQLName authid = this.exprParser.name();
4487                 stmt.setAuthid(authid);
4488             }
4489 
4490             break;
4491         }
4492 
4493         SQLStatement block;
4494         if (lexer.token() == Token.BEGIN) {
4495             block = this.parseBlock();
4496         } else {
4497             block = this.parseStatement();
4498         }
4499 
4500         stmt.setBlock(block);
4501 
4502         return stmt;
4503     }
4504 
4505     /**
4506      * parse create procedure parameters
4507      *
4508      * @param parameters
4509      */
4510     private void parserParameters(List!(SQLParameter) parameters, SQLObject parent) {
4511         if (lexer.token() == Token.RPAREN) {
4512             return;
4513         }
4514 
4515         for (; ; ) {
4516             SQLParameter parameter = new SQLParameter();
4517 
4518             if (lexer.token() == Token.CURSOR) {
4519                 lexer.nextToken();
4520 
4521                 parameter.setName(this.exprParser.name());
4522 
4523                 accept(Token.IS);
4524                 SQLSelect select = this.createSQLSelectParser().select();
4525 
4526                 SQLDataTypeImpl dataType = new SQLDataTypeImpl();
4527                 dataType.setName("CURSOR");
4528                 parameter.setDataType(dataType);
4529 
4530                 parameter.setDefaultValue(new SQLQueryExpr(select));
4531 
4532             } else if (lexer.token() == Token.IN || lexer.token() == Token.OUT || lexer.token() == Token.INOUT) {
4533 
4534                 if (lexer.token() == Token.IN) {
4535                     parameter.setParamType(SQLParameter.ParameterType.IN);
4536                 } else if (lexer.token() == Token.OUT) {
4537                     parameter.setParamType(SQLParameter.ParameterType.OUT);
4538                 } else if (lexer.token() == Token.INOUT) {
4539                     parameter.setParamType(SQLParameter.ParameterType.INOUT);
4540                 }
4541                 lexer.nextToken();
4542 
4543                 parameter.setName(this.exprParser.name());
4544 
4545                 parameter.setDataType(this.exprParser.parseDataType());
4546             } else {
4547                 parameter.setParamType(SQLParameter.ParameterType.DEFAULT);// default parameter type is in
4548                 parameter.setName(this.exprParser.name());
4549                 parameter.setDataType(this.exprParser.parseDataType());
4550 
4551                 if (lexer.token() == Token.COLONEQ) {
4552                     lexer.nextToken();
4553                     parameter.setDefaultValue(this.exprParser.expr());
4554                 }
4555             }
4556 
4557             parameters.add(parameter);
4558             if (lexer.token() == Token.COMMA || lexer.token() == Token.SEMI) {
4559                 lexer.nextToken();
4560             }
4561 
4562             if (lexer.token() != Token.BEGIN && lexer.token() != Token.RPAREN) {
4563                 continue;
4564             }
4565 
4566             break;
4567         }
4568     }
4569 
4570     /**
4571      * parse procedure statement block
4572      *
4573      * @param statementList
4574      */
4575     private void parseProcedureStatementList(List!(SQLStatement) statementList) {
4576         parseProcedureStatementList(statementList, -1);
4577     }
4578 
4579     /**
4580      * parse procedure statement block
4581      */
4582     private void parseProcedureStatementList(List!(SQLStatement) statementList, int max) {
4583 
4584         for (; ; ) {
4585             if (max != -1) {
4586                 if (statementList.size() >= max) {
4587                     return;
4588                 }
4589             }
4590 
4591             if (lexer.token() == Token.EOF) {
4592                 return;
4593             }
4594             if (lexer.token() == Token.END) {
4595                 return;
4596             }
4597             if (lexer.token() == Token.ELSE) {
4598                 return;
4599             }
4600             if (lexer.token() == (Token.SEMI)) {
4601                 lexer.nextToken();
4602                 continue;
4603             }
4604             if (lexer.token() == Token.WHEN) {
4605                 return;
4606             }
4607             if (lexer.token() == Token.UNTIL) {
4608                 return;
4609             }
4610             // select into
4611             if (lexer.token() == (Token.SELECT)) {
4612                 statementList.add(this.parseSelectInto());
4613                 continue;
4614             }
4615 
4616             // update
4617             if (lexer.token() == (Token.UPDATE)) {
4618                 statementList.add(parseUpdateStatement());
4619                 continue;
4620             }
4621 
4622             // create
4623             if (lexer.token() == (Token.CREATE)) {
4624                 statementList.add(parseCreate());
4625                 continue;
4626             }
4627 
4628             // insert
4629             if (lexer.token() == Token.INSERT) {
4630                 SQLStatement stmt = parseInsert();
4631                 statementList.add(stmt);
4632                 continue;
4633             }
4634 
4635             // delete
4636             if (lexer.token() == (Token.DELETE)) {
4637                 statementList.add(parseDeleteStatement());
4638                 continue;
4639             }
4640 
4641             // call
4642             if (lexer.token() == Token.LBRACE || lexer.identifierEquals("CALL")) {
4643                 statementList.add(this.parseCall());
4644                 continue;
4645             }
4646 
4647             // begin
4648             if (lexer.token() == Token.BEGIN) {
4649                 statementList.add(this.parseBlock());
4650                 continue;
4651             }
4652 
4653             if (lexer.token() == Token.VARIANT) {
4654                 SQLExpr variant = this.exprParser.primary();
4655                 if (cast(SQLBinaryOpExpr)(variant) !is null) {
4656                     SQLBinaryOpExpr binaryOpExpr = cast(SQLBinaryOpExpr) variant;
4657                     if (binaryOpExpr.getOperator() == SQLBinaryOperator.Assignment) {
4658                         SQLSetStatement stmt = new SQLSetStatement(binaryOpExpr.getLeft(), binaryOpExpr.getRight(),
4659                                 getDbType());
4660                         statementList.add(stmt);
4661                         continue;
4662                     }
4663                 }
4664                 accept(Token.COLONEQ);
4665                 SQLExpr value = this.exprParser.expr();
4666 
4667                 SQLSetStatement stmt = new SQLSetStatement(variant, value, getDbType());
4668                 statementList.add(stmt);
4669                 continue;
4670             }
4671 
4672             // select
4673             if (lexer.token() == Token.LPAREN) {
4674                 char ch = lexer.current();
4675                 int bp = lexer.bp();
4676                 lexer.nextToken();
4677 
4678                 if (lexer.token() == Token.SELECT) {
4679                     lexer.reset(bp, ch, Token.LPAREN);
4680                     statementList.add(this.parseSelect());
4681                     continue;
4682                 } else {
4683                     throw new ParserException("TODO. " ~ lexer.info());
4684                 }
4685             }
4686             // assign statement
4687             if (lexer.token() == Token.SET) {
4688                 statementList.add(this.parseAssign());
4689                 continue;
4690             }
4691 
4692             // while statement
4693             if (lexer.token() == Token.WHILE) {
4694                 SQLStatement stmt = this.parseWhile();
4695                 statementList.add(stmt);
4696                 continue;
4697             }
4698 
4699             // loop statement
4700             if (lexer.token() == Token.LOOP) {
4701                 statementList.add(this.parseLoop());
4702                 continue;
4703             }
4704 
4705             // if statement
4706             if (lexer.token() == Token.IF) {
4707                 statementList.add(this.parseIf());
4708                 continue;
4709             }
4710 
4711             // case statement
4712             if (lexer.token() == Token.CASE) {
4713                 statementList.add(this.parseCase());
4714                 continue;
4715             }
4716 
4717             // declare statement
4718             if (lexer.token() == Token.DECLARE) {
4719                 SQLStatement stmt = this.parseDeclare();
4720                 statementList.add(stmt);
4721                 continue;
4722             }
4723 
4724             // leave statement
4725             if (lexer.token() == Token.LEAVE) {
4726                 statementList.add(this.parseLeave());
4727                 continue;
4728             }
4729 
4730             // iterate statement
4731             if (lexer.token() == Token.ITERATE) {
4732                 statementList.add(this.parseIterate());
4733                 continue;
4734             }
4735 
4736             // repeat statement
4737             if (lexer.token() == Token.REPEAT) {
4738                 statementList.add(this.parseRepeat());
4739                 continue;
4740             }
4741 
4742             // open cursor
4743             if (lexer.token() == Token.OPEN) {
4744                 statementList.add(this.parseOpen());
4745                 continue;
4746             }
4747 
4748             // close cursor
4749             if (lexer.token() == Token.CLOSE) {
4750                 statementList.add(this.parseClose());
4751                 continue;
4752             }
4753 
4754             // fetch cursor into
4755             if (lexer.token() == Token.FETCH) {
4756                 statementList.add(this.parseFetch());
4757                 continue;
4758             }
4759 
4760             if (lexer.identifierEquals(FnvHash.Constants.CHECKSUM)) {
4761                 statementList.add(this.parseChecksum());
4762                 continue;
4763             }
4764 
4765             if (lexer.token() == Token.IDENTIFIER) {
4766                 string label = lexer.stringVal();
4767                 char ch = lexer.current();
4768                 int bp = lexer.bp();
4769                 lexer.nextToken();
4770                 if (lexer.token() == Token.VARIANT && lexer.stringVal() == ":") {
4771                     lexer.nextToken();
4772                     if (lexer.token() == Token.LOOP) {
4773                         // parse loop statement
4774                         statementList.add(this.parseLoop(label));
4775                     } else if (lexer.token() == Token.WHILE) {
4776                         // parse while statement with label
4777                         statementList.add(this.parseWhile(label));
4778                     } else if (lexer.token() == Token.BEGIN) {
4779                         // parse begin-end statement with label
4780                         statementList.add(this.parseBlock(label));
4781                     } else if (lexer.token() == Token.REPEAT) {
4782                         // parse repeat statement with label
4783                         statementList.add(this.parseRepeat(label));
4784                     }
4785                     continue;
4786                 } else {
4787                     lexer.reset(bp, ch, Token.IDENTIFIER);
4788                 }
4789 
4790             }
4791             throw new ParserException("TODO, " ~ lexer.info());
4792         }
4793 
4794     }
4795 
4796 
4797     public MySqlChecksumTableStatement parseChecksum() {
4798         MySqlChecksumTableStatement stmt = new MySqlChecksumTableStatement();
4799         if (lexer.identifierEquals(FnvHash.Constants.CHECKSUM)) {
4800             lexer.nextToken();
4801         } else {
4802             throw new ParserException("TODO " ~ lexer.info());
4803         }
4804 
4805         for (;;) {
4806             SQLExprTableSource table = cast(SQLExprTableSource) this.createSQLSelectParser().parseTableSource();
4807             stmt.addTable(table);
4808 
4809             if (lexer.token() == Token.COMMA) {
4810                 lexer.nextToken();
4811                 continue;
4812             }
4813 
4814             break;
4815         }
4816 
4817         return stmt;
4818     }
4819 
4820     /**
4821      * parse if statement
4822      *
4823      * @return MySqlIfStatement
4824      */
4825     override public SQLIfStatement parseIf() {
4826         accept(Token.IF);
4827 
4828         SQLIfStatement stmt = new SQLIfStatement();
4829 
4830         stmt.setCondition(this.exprParser.expr());
4831 
4832         accept(Token.THEN);
4833 
4834         this.parseStatementList(stmt.getStatements(), -1, stmt);
4835 
4836         while (lexer.token() == Token.ELSE) {
4837             lexer.nextToken();
4838 
4839             if (lexer.token() == Token.IF) {
4840                 lexer.nextToken();
4841 
4842                 SQLIfStatement.ElseIf elseIf = new SQLIfStatement.ElseIf();
4843 
4844                 elseIf.setCondition(this.exprParser.expr());
4845                 elseIf.setParent(stmt);
4846 
4847                 accept(Token.THEN);
4848                 this.parseStatementList(elseIf.getStatements(), -1, elseIf);
4849 
4850 
4851                 stmt.getElseIfList().add(elseIf);
4852             } else {
4853                 SQLIfStatement.Else elseItem = new SQLIfStatement.Else();
4854                 this.parseStatementList(elseItem.getStatements(), -1, elseItem);
4855                 stmt.setElseItem(elseItem);
4856                 break;
4857             }
4858         }
4859 
4860         accept(Token.END);
4861         accept(Token.IF);
4862         accept(Token.SEMI);
4863         stmt.setAfterSemi(true);
4864 
4865         return stmt;
4866     }
4867 
4868     /**
4869      * parse while statement
4870      *
4871      * @return MySqlWhileStatement
4872      */
4873     override public SQLWhileStatement parseWhile() {
4874         accept(Token.WHILE);
4875         SQLWhileStatement stmt = new SQLWhileStatement();
4876 
4877         stmt.setCondition(this.exprParser.expr());
4878 
4879         accept(Token.DO);
4880 
4881         this.parseStatementList(stmt.getStatements(), -1, stmt);
4882 
4883         accept(Token.END);
4884 
4885         accept(Token.WHILE);
4886 
4887         accept(Token.SEMI);
4888 
4889         return stmt;
4890 
4891     }
4892 
4893     /**
4894      * parse while statement with label
4895      *
4896      * @return MySqlWhileStatement
4897      */
4898     public SQLWhileStatement parseWhile(string label) {
4899         accept(Token.WHILE);
4900 
4901         SQLWhileStatement stmt = new SQLWhileStatement();
4902 
4903         stmt.setLabelName(label);
4904 
4905         stmt.setCondition(this.exprParser.expr());
4906 
4907         accept(Token.DO);
4908 
4909         this.parseStatementList(stmt.getStatements(), -1, stmt);
4910 
4911         accept(Token.END);
4912 
4913         accept(Token.WHILE);
4914 
4915         acceptIdentifier(label);
4916 
4917         accept(Token.SEMI);
4918 
4919         return stmt;
4920 
4921     }
4922 
4923     /**
4924      * parse case statement
4925      *
4926      * @return MySqlCaseStatement
4927      */
4928     override public MySqlCaseStatement parseCase() {
4929         MySqlCaseStatement stmt = new MySqlCaseStatement();
4930         accept(Token.CASE);
4931 
4932         if (lexer.token() == Token.WHEN)// grammar 1
4933         {
4934             while (lexer.token() == Token.WHEN) {
4935 
4936                 MySqlCaseStatement.MySqlWhenStatement when = new MySqlCaseStatement.MySqlWhenStatement();
4937                 // when expr
4938                 when.setCondition(exprParser.expr());
4939 
4940                 accept(Token.THEN);
4941 
4942                 // when block
4943                 this.parseStatementList(when.getStatements(), -1, when);
4944 
4945                 stmt.addWhenStatement(when);
4946             }
4947             if (lexer.token() == Token.ELSE) {
4948                 // parse else block
4949                 SQLIfStatement.Else elseStmt = new SQLIfStatement.Else();
4950                 this.parseStatementList(elseStmt.getStatements(), -1, elseStmt);
4951                 stmt.setElseItem(elseStmt);
4952             }
4953         } else// grammar 2
4954         {
4955             // case expr
4956             stmt.setCondition(exprParser.expr());
4957 
4958             while (lexer.token() == Token.WHEN) {
4959                 accept(Token.WHEN);
4960                 MySqlCaseStatement.MySqlWhenStatement when = new MySqlCaseStatement.MySqlWhenStatement();
4961                 // when expr
4962                 when.setCondition(exprParser.expr());
4963 
4964                 accept(Token.THEN);
4965 
4966                 // when block
4967                 this.parseStatementList(when.getStatements(), -1, when);
4968 
4969                 stmt.addWhenStatement(when);
4970             }
4971             if (lexer.token() == Token.ELSE) {
4972                 accept(Token.ELSE);
4973                 // else block
4974                 SQLIfStatement.Else elseStmt = new SQLIfStatement.Else();
4975                 this.parseStatementList(elseStmt.getStatements(), -1, elseStmt);
4976                 stmt.setElseItem(elseStmt);
4977             }
4978         }
4979         accept(Token.END);
4980         accept(Token.CASE);
4981         accept(Token.SEMI);
4982         return stmt;
4983 
4984     }
4985 
4986     /**
4987      * parse declare statement
4988      */
4989     override public SQLStatement parseDeclare() {
4990         char markChar = lexer.current();
4991         int markBp = lexer.bp();
4992 
4993         lexer.nextToken();
4994 
4995         if (lexer.token() == Token.CONTINUE) {
4996             lexer.reset(markBp, markChar, Token.DECLARE);
4997             return this.parseDeclareHandler();
4998         }
4999 
5000         lexer.nextToken();
5001         if (lexer.token() == Token.CURSOR) {
5002             lexer.reset(markBp, markChar, Token.DECLARE);
5003             return this.parseCursorDeclare();
5004         } else if (lexer.identifierEquals("HANDLER")) {
5005             //DECLARE异常处理程序 [add by zhujun 2016-04-16]
5006             lexer.reset(markBp, markChar, Token.DECLARE);
5007             return this.parseDeclareHandler();
5008         } else if (lexer.token() == Token.CONDITION) {
5009             //DECLARE异常 [add by zhujun 2016-04-17]
5010             lexer.reset(markBp, markChar, Token.DECLARE);
5011             return this.parseDeclareCondition();
5012         } else {
5013             lexer.reset(markBp, markChar, Token.DECLARE);
5014         }
5015 
5016         MySqlDeclareStatement stmt = new MySqlDeclareStatement();
5017         accept(Token.DECLARE);
5018         // lexer.nextToken();
5019         for (; ; ) {
5020             SQLDeclareItem item = new SQLDeclareItem();
5021             item.setName(exprParser.name());
5022 
5023             stmt.addVar(item);
5024             if (lexer.token() == Token.COMMA) {
5025                 lexer.nextToken();
5026                 stmt.setAfterSemi(true);
5027                 continue;
5028             } else if (lexer.token() != Token.EOF) {
5029                 // var type
5030                 item.setDataType(exprParser.parseDataType());
5031 
5032                 if (lexer.token() == Token.DEFAULT) {
5033                     lexer.nextToken();
5034                     SQLExpr defaultValue = this.exprParser.primary();
5035                     item.setValue(defaultValue);
5036                 }
5037 
5038                 break;
5039             } else {
5040                 throw new ParserException("TODO. " ~ lexer.info());
5041             }
5042         }
5043         return stmt;
5044     }
5045 
5046     /**
5047      * parse assign statement
5048      */
5049     public SQLSetStatement parseAssign() {
5050         accept(Token.SET);
5051         SQLSetStatement stmt = new SQLSetStatement(getDbType());
5052         parseAssignItems(stmt.getItems(), stmt);
5053         return stmt;
5054     }
5055 
5056     /**
5057      * parse select into
5058      */
5059     public MySqlSelectIntoStatement parseSelectInto() {
5060         MySqlSelectIntoParser parse = new MySqlSelectIntoParser(this.exprParser);
5061         return parse.parseSelectInto();
5062     }
5063 
5064     /**
5065      * parse loop statement
5066      */
5067     public SQLLoopStatement parseLoop() {
5068         SQLLoopStatement loopStmt = new SQLLoopStatement();
5069         accept(Token.LOOP);
5070         this.parseStatementList(loopStmt.getStatements(), -1, loopStmt);
5071         accept(Token.END);
5072         accept(Token.LOOP);
5073         accept(Token.SEMI);
5074         loopStmt.setAfterSemi(true);
5075         return loopStmt;
5076     }
5077 
5078     /**
5079      * parse loop statement with label
5080      */
5081     public SQLLoopStatement parseLoop(string label) {
5082         SQLLoopStatement loopStmt = new SQLLoopStatement();
5083         loopStmt.setLabelName(label);
5084         accept(Token.LOOP);
5085         this.parseStatementList(loopStmt.getStatements(), -1, loopStmt);
5086         accept(Token.END);
5087         accept(Token.LOOP);
5088         if (lexer.token() != Token.SEMI) {
5089             acceptIdentifier(label);
5090         }
5091         accept(Token.SEMI);
5092         loopStmt.setAfterSemi(true);
5093         return loopStmt;
5094     }
5095 
5096     /**
5097      * parse loop statement with label
5098      */
5099     public SQLBlockStatement parseBlock(string label) {
5100         SQLBlockStatement block = new SQLBlockStatement();
5101         block.setLabelName(label);
5102         accept(Token.BEGIN);
5103         this.parseStatementList(block.getStatementList(), -1, block);
5104         accept(Token.END);
5105         acceptIdentifier(label);
5106         return block;
5107     }
5108 
5109     /**
5110      * parse leave statement
5111      */
5112     override public MySqlLeaveStatement parseLeave() {
5113         accept(Token.LEAVE);
5114         MySqlLeaveStatement leaveStmt = new MySqlLeaveStatement();
5115         leaveStmt.setLabelName(exprParser.name().getSimpleName());
5116         accept(Token.SEMI);
5117         return leaveStmt;
5118     }
5119 
5120     /**
5121      * parse iterate statement
5122      */
5123     public MySqlIterateStatement parseIterate() {
5124         accept(Token.ITERATE);
5125         MySqlIterateStatement iterateStmt = new MySqlIterateStatement();
5126         iterateStmt.setLabelName(exprParser.name().getSimpleName());
5127         accept(Token.SEMI);
5128         return iterateStmt;
5129     }
5130 
5131     /**
5132      * parse repeat statement
5133      *
5134      * @return
5135      */
5136     override public MySqlRepeatStatement parseRepeat() {
5137         MySqlRepeatStatement stmt = new MySqlRepeatStatement();
5138         accept(Token.REPEAT);
5139         parseStatementList(stmt.getStatements(), -1, stmt);
5140         accept(Token.UNTIL);
5141         stmt.setCondition(exprParser.expr());
5142         accept(Token.END);
5143         accept(Token.REPEAT);
5144         accept(Token.SEMI);
5145         stmt.setAfterSemi(true);
5146         return stmt;
5147     }
5148 
5149     /**
5150      * parse repeat statement with label
5151      *
5152      * @param label
5153      * @return
5154      */
5155     public MySqlRepeatStatement parseRepeat(string label) {
5156         MySqlRepeatStatement repeatStmt = new MySqlRepeatStatement();
5157         repeatStmt.setLabelName(label);
5158         accept(Token.REPEAT);
5159         this.parseStatementList(repeatStmt.getStatements(), -1, repeatStmt);
5160         accept(Token.UNTIL);
5161         repeatStmt.setCondition(exprParser.expr());
5162         accept(Token.END);
5163         accept(Token.REPEAT);
5164         acceptIdentifier(label);
5165         accept(Token.SEMI);
5166         return repeatStmt;
5167     }
5168 
5169     /**
5170      * parse cursor declare statement
5171      *
5172      * @return
5173      */
5174     public MySqlCursorDeclareStatement parseCursorDeclare() {
5175         MySqlCursorDeclareStatement stmt = new MySqlCursorDeclareStatement();
5176         accept(Token.DECLARE);
5177 
5178         stmt.setCursorName(exprParser.name());
5179 
5180         accept(Token.CURSOR);
5181 
5182         accept(Token.FOR);
5183 
5184         //SQLSelectStatement selelctStmt = cast(SQLSelectStatement) parseSelect();
5185         SQLSelect select = this.createSQLSelectParser().select();
5186         stmt.setSelect(select);
5187 
5188         accept(Token.SEMI);
5189 
5190         return stmt;
5191     }
5192 
5193     /**
5194      * zhujun [455910092@qq.com]
5195      * parse spstatement
5196      *
5197      * @return
5198      */
5199     public SQLStatement parseSpStatement() {
5200 
5201         // update
5202         if (lexer.token() == (Token.UPDATE)) {
5203             return parseUpdateStatement();
5204         }
5205 
5206         // create
5207         if (lexer.token() == (Token.CREATE)) {
5208             return parseCreate();
5209         }
5210 
5211         // insert
5212         if (lexer.token() == Token.INSERT) {
5213             return parseInsert();
5214         }
5215 
5216         // delete
5217         if (lexer.token() == (Token.DELETE)) {
5218             return parseDeleteStatement();
5219         }
5220 
5221         // begin
5222         if (lexer.token() == Token.BEGIN) {
5223             return this.parseBlock();
5224         }
5225 
5226         // select
5227         if (lexer.token() == Token.LPAREN) {
5228             char ch = lexer.current();
5229             int bp = lexer.bp();
5230             lexer.nextToken();
5231 
5232             if (lexer.token() == Token.SELECT) {
5233                 lexer.reset(bp, ch, Token.LPAREN);
5234                 return this.parseSelect();
5235             } else {
5236                 throw new ParserException("TODO. " ~ lexer.info());
5237             }
5238         }
5239         // assign statement
5240         if (lexer.token() == Token.SET) {
5241             return parseAssign();
5242         }
5243 
5244         throw new ParserException("error sp_statement. " ~ lexer.info());
5245     }
5246 
5247     /**
5248      * 定义异常处理程序
5249      *
5250      * @return
5251      */
5252     public MySqlDeclareHandlerStatement parseDeclareHandler() {
5253         //DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement
5254         //handler_type 取值为 CONTINUE | EXIT | UNDO
5255         //condition_value 取值为 SQLWARNING | NOT FOUND | SQLEXCEPTION | SQLSTATE value(异常码 e.g 1062)
5256 
5257         MySqlDeclareHandlerStatement stmt = new MySqlDeclareHandlerStatement();
5258         accept(Token.DECLARE);
5259         //string handlerType = exprParser.name().getSimpleName();
5260         if (lexer.token() == Token.CONTINUE) {
5261             stmt.setHandleType(MySqlHandlerType.CONTINUE);
5262         } else if (lexer.token() == Token.EXIT) {
5263             stmt.setHandleType(MySqlHandlerType.CONTINUE);
5264         } else if (lexer.token() == Token.UNDO) {
5265             stmt.setHandleType(MySqlHandlerType.CONTINUE);
5266         } else {
5267             throw new ParserException("unkown handle type. " ~ lexer.info());
5268         }
5269         lexer.nextToken();
5270 
5271         acceptIdentifier("HANDLER");
5272 
5273         accept(Token.FOR);
5274 
5275         for (; ; ) {
5276             string tokenName = lexer.stringVal();
5277             ConditionValue condition = new ConditionValue();
5278 
5279             if (equalsIgnoreCase(tokenName, "NOT")) {//for 'NOT FOUND'
5280                 lexer.nextToken();
5281                 acceptIdentifier("FOUND");
5282                 condition.setType(ConditionValue.ConditionType.SYSTEM);
5283                 condition.setValue("NOT FOUND");
5284 
5285             } else if (equalsIgnoreCase(tokenName, "SQLSTATE")) { //for SQLSTATE (SQLSTATE '10001')
5286                 condition.setType(ConditionValue.ConditionType.SQLSTATE);
5287                 lexer.nextToken();
5288                 //condition.setValue(lexer.stringVal());
5289                 //lexer.nextToken();
5290                 condition.setValue((cast(Object)(exprParser.name())).toString());
5291             } else if (lexer.identifierEquals("SQLEXCEPTION")) { //for SQLEXCEPTION
5292                 condition.setType(ConditionValue.ConditionType.SYSTEM);
5293                 condition.setValue(lexer.stringVal());
5294                 lexer.nextToken();
5295             } else if (lexer.identifierEquals("SQLWARNING")) { //for SQLWARNING
5296                 condition.setType(ConditionValue.ConditionType.SYSTEM);
5297                 condition.setValue(lexer.stringVal());
5298                 lexer.nextToken();
5299             } else { //for condition_name or mysql_error_code
5300                 if (lexer.token() == Token.LITERAL_INT) {
5301                     condition.setType(ConditionValue.ConditionType.MYSQL_ERROR_CODE);
5302                     condition.setValue((cast(Object)(lexer.integerValue())).toString());
5303                 } else {
5304                     condition.setType(ConditionValue.ConditionType.SELF);
5305                     condition.setValue(tokenName);
5306                 }
5307                 lexer.nextToken();
5308             }
5309             stmt.getConditionValues().add(condition);
5310             if (lexer.token() == Token.COMMA) {
5311                 accept(Token.COMMA);
5312                 continue;
5313             } else if (lexer.token() != Token.EOF) {
5314                 break;
5315             } else {
5316                 throw new ParserException("declare handle not eof");
5317             }
5318         }
5319 
5320         stmt.setSpStatement(parseSpStatement());
5321 
5322         if (!(cast(SQLBlockStatement)(stmt.getSpStatement()) !is null )) {
5323             accept(Token.SEMI);
5324         }
5325 
5326 
5327         return stmt;
5328     }
5329 
5330     /**
5331      * zhujun [455910092@qq.com]
5332      * 2016-04-17
5333      * 定义条件
5334      *
5335      * @return
5336      */
5337     public MySqlDeclareConditionStatement parseDeclareCondition() {
5338         /*
5339         DECLARE condition_name CONDITION FOR condition_value
5340 
5341     	condition_value:
5342     	    SQLSTATE [VALUE] sqlstate_value
5343     	  | mysql_error_code
5344     	*/
5345         MySqlDeclareConditionStatement stmt = new MySqlDeclareConditionStatement();
5346         accept(Token.DECLARE);
5347 
5348         stmt.setConditionName((cast(Object)(exprParser.name())).toString());
5349 
5350         accept(Token.CONDITION);
5351 
5352         accept(Token.FOR);
5353 
5354         string tokenName = lexer.stringVal();
5355         ConditionValue condition = new ConditionValue();
5356         if (equalsIgnoreCase(tokenName, "SQLSTATE")) { //for SQLSTATE (SQLSTATE '10001')
5357             condition.setType(ConditionValue.ConditionType.SQLSTATE);
5358             lexer.nextToken();
5359             condition.setValue((cast(Object)exprParser.name()).toString());
5360         } else if (lexer.token() == Token.LITERAL_INT) {
5361             condition.setType(ConditionValue.ConditionType.MYSQL_ERROR_CODE);
5362             condition.setValue(lexer.integerValue().toString());
5363             lexer.nextToken();
5364         } else {
5365             throw new ParserException("declare condition grammer error. " ~ lexer.info());
5366         }
5367 
5368         stmt.setConditionValue(condition);
5369 
5370         accept(Token.SEMI);
5371 
5372         return stmt;
5373     }
5374 }