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.MySqlExprParser;
17 
18 import hunt.sql.ast;
19 import hunt.sql.ast.expr.SQLAggregateExpr;
20 import hunt.sql.ast.expr.SQLBinaryOpExpr;
21 import hunt.sql.ast.expr.SQLBinaryOperator;
22 import hunt.sql.ast.expr.SQLCharExpr;
23 import hunt.sql.ast.expr.SQLHexExpr;
24 import hunt.sql.ast.expr.SQLIdentifierExpr;
25 import hunt.sql.ast.expr.SQLMethodInvokeExpr;
26 import hunt.sql.ast.expr.SQLUnaryExpr;
27 import hunt.sql.ast.expr.SQLUnaryOperator;
28 import hunt.sql.ast.expr.SQLVariantRefExpr;
29 import hunt.sql.ast.statement.SQLAssignItem;
30 import hunt.sql.ast.statement.SQLColumnDefinition;
31 import hunt.sql.ast.statement.SQLForeignKeyImpl;
32 import hunt.sql.dialect.mysql.ast.MySqlPrimaryKey;
33 import hunt.sql.dialect.mysql.ast.MySqlUnique;
34 import hunt.sql.dialect.mysql.ast.MysqlForeignKey;
35 import hunt.sql.ast.statement.SQLForeignKeyImpl;
36 import hunt.sql.dialect.mysql.ast.expr.MySqlCharExpr;
37 import hunt.sql.dialect.mysql.ast.expr.MySqlExtractExpr;
38 import hunt.sql.ast.expr.SQLIntervalExpr;
39 import hunt.sql.ast.expr.SQLIntervalUnit;
40 import hunt.sql.dialect.mysql.ast.expr.MySqlMatchAgainstExpr;
41 // import hunt.sql.dialect.mysql.ast.expr.MySqlMatchAgainstExpr.SearchModifier;
42 import hunt.sql.dialect.mysql.ast.expr.MySqlOrderingExpr;
43 import hunt.sql.dialect.mysql.ast.expr.MySqlOutFileExpr;
44 import hunt.sql.dialect.mysql.ast.expr.MySqlUserName;
45 import hunt.sql.parser;
46 import hunt.sql.util.FnvHash;
47 import hunt.sql.util.DBType;
48 import hunt.sql.dialect.mysql.parser.MySqlLexer;
49 import hunt.String;
50 import std.string;
51 import hunt.sql.util.Utils;
52 import hunt.String;
53 import hunt.sql.dialect.mysql.parser.MySqlSelectParser;
54 import hunt.text;
55 
56 import std.concurrency : initOnce;
57 
58 public class MySqlExprParser : SQLExprParser {
59 
60     private enum string[] strings = ["AVG", "COUNT", "GROUP_CONCAT", "MAX", "MIN", "STDDEV", "SUM"];
61 
62     static string[] AGGREGATE_FUNCTIONS() {
63         __gshared string[] inst;
64         return initOnce!inst({
65             long[] codes = AGGREGATE_FUNCTIONS_CODES();
66             string[] r = new string[codes.length];
67             
68             foreach(string str ; strings) {
69                 long hash = FnvHash.fnv1a_64_lower(str);
70                 int index = search(codes, hash);
71                 r[index] = str;
72             }
73             return r;
74         }());
75     }
76 
77     static long[] AGGREGATE_FUNCTIONS_CODES() {
78         __gshared long[] inst;
79         return initOnce!inst({
80             return FnvHash.fnv1a_64_lower(strings, true);
81         }());
82     }
83 
84     // public  static string[] AGGREGATE_FUNCTIONS;
85 
86     // public  static long[] AGGREGATE_FUNCTIONS_CODES;
87 
88     // static this() {
89     //     string[] strings = [ "AVG", "COUNT", "GROUP_CONCAT", "MAX", "MIN", "STDDEV", "SUM" ];
90     //     AGGREGATE_FUNCTIONS_CODES = FnvHash.fnv1a_64_lower(strings, true);
91     //     AGGREGATE_FUNCTIONS = new string[AGGREGATE_FUNCTIONS_CODES.length];
92     //     foreach(string str ; strings) {
93     //         long hash = FnvHash.fnv1a_64_lower(str);
94     //         int index = search(AGGREGATE_FUNCTIONS_CODES, hash);
95     //         AGGREGATE_FUNCTIONS[index] = str;
96     //     }
97     // }
98 
99     public this(Lexer lexer){
100         super(lexer, DBType.MYSQL.name);
101         this.aggregateFunctions = AGGREGATE_FUNCTIONS;
102         this.aggregateFunctionHashCodes = AGGREGATE_FUNCTIONS_CODES;
103     }
104 
105     public this(string sql){
106         this(new MySqlLexer(sql));
107         this.lexer.nextToken();
108         import std.stdio;
109     }
110 
111     public this(string sql, SQLParserFeature[] features...){
112         super(new MySqlLexer(sql, features), DBType.MYSQL.name);
113         this.aggregateFunctions = AGGREGATE_FUNCTIONS;
114         this.aggregateFunctionHashCodes = AGGREGATE_FUNCTIONS_CODES;
115         if (sql.length > 6) {
116             char c0 = charAt(sql, 0);
117             char c1 = charAt(sql, 1);
118             char c2 = charAt(sql, 2);
119             char c3 = charAt(sql, 3);
120             char c4 = charAt(sql, 4);
121             char c5 = charAt(sql, 5);
122             char c6 = charAt(sql, 6);
123 
124             if (c0 == 'S' && c1 == 'E' && c2 == 'L' && c3 == 'E' && c4 == 'C' && c5 == 'T' && c6 == ' ') {
125                 lexer.reset(6, ' ', Token.SELECT);
126                 return;
127             }
128 
129             if (c0 == 's' && c1 == 'e' && c2 == 'l' && c3 == 'e' && c4 == 'c' && c5 == 't' && c6 == ' ') {
130                 lexer.reset(6, ' ', Token.SELECT);
131                 return;
132             }
133 
134             if (c0 == 'I' && c1 == 'N' && c2 == 'S' && c3 == 'E' && c4 == 'R' && c5 == 'T' && c6 == ' ') {
135                 lexer.reset(6, ' ', Token.INSERT);
136                 return;
137             }
138 
139             if (c0 == 'i' && c1 == 'n' && c2 == 's' && c3 == 'e' && c4 == 'r' && c5 == 't' && c6 == ' ') {
140                 lexer.reset(6, ' ', Token.INSERT);
141                 return;
142             }
143 
144             if (c0 == 'U' && c1 == 'P' && c2 == 'D' && c3 == 'A' && c4 == 'T' && c5 == 'E' && c6 == ' ') {
145                 lexer.reset(6, ' ', Token.UPDATE);
146                 return;
147             }
148 
149             if (c0 == 'u' && c1 == 'p' && c2 == 'd' && c3 == 'a' && c4 == 't' && c5 == 'e' && c6 == ' ') {
150                 lexer.reset(6, ' ', Token.UPDATE);
151                 return;
152             }
153 
154             if (c0 == '/' && c1 == '*' && isEnabled(SQLParserFeature.OptimizedForParameterized)) {
155                 MySqlLexer mySqlLexer = cast(MySqlLexer) lexer;
156                 mySqlLexer.skipFirstHintsOrMultiCommentAndNextToken();
157                 return;
158             }
159         }
160         this.lexer.nextToken();
161 
162     }
163 
164     public this(string sql, bool keepComments){
165         this(new MySqlLexer(sql, true, keepComments));
166         this.lexer.nextToken();
167     }
168 
169 
170     public this(string sql, bool skipComment,bool keepComments){
171         this(new MySqlLexer(sql, skipComment, keepComments));
172         this.lexer.nextToken();
173     }
174 
175     override public SQLExpr primary() {
176          Token tok = lexer.token();
177 
178         if (lexer.identifierEquals(FnvHash.Constants.OUTFILE)) {
179             lexer.nextToken();
180             SQLExpr file = primary();
181             SQLExpr expr = new MySqlOutFileExpr(file);
182 
183             return primaryRest(expr);
184 
185         }
186 
187         switch (tok) {
188             case Token.VARIANT:
189                 SQLVariantRefExpr varRefExpr = new SQLVariantRefExpr(lexer.stringVal());
190                 lexer.nextToken();
191                 if (varRefExpr.getName().equalsIgnoreCase("@@global")) {
192                     accept(Token.DOT);
193                     varRefExpr = new SQLVariantRefExpr(lexer.stringVal(), true);
194                     lexer.nextToken();
195                 } else if (varRefExpr.getName() == "@" && lexer.token() == Token.LITERAL_CHARS) {
196                     varRefExpr.setName("@'" ~ lexer.stringVal() ~ "'");
197                     lexer.nextToken();
198                 } else if (varRefExpr.getName() == "@@" && lexer.token() == Token.LITERAL_CHARS) {
199                     varRefExpr.setName("@@'" ~ lexer.stringVal() ~ "'");
200                     lexer.nextToken();
201                 }
202                 return primaryRest(varRefExpr);
203             case Token.VALUES:
204                 lexer.nextToken();
205                 if (lexer.token() != Token.LPAREN) {
206                     throw new ParserException("syntax error, illegal values clause. " ~ lexer.info());
207                 }
208                 return this.methodRest(new SQLIdentifierExpr("VALUES"), true);
209             case Token.BINARY:
210                 lexer.nextToken();
211                 if (lexer.token() == Token.COMMA || lexer.token() == Token.SEMI || lexer.token() == Token.EOF) {
212                     return new SQLIdentifierExpr("BINARY");
213                 } else {
214                     SQLUnaryExpr binaryExpr = new SQLUnaryExpr(SQLUnaryOperator.BINARY, expr());
215                     return primaryRest(binaryExpr);
216                 }
217             default:
218                 return super.primary();
219         }
220 
221     }
222 
223     override public  SQLExpr primaryRest(SQLExpr expr) {
224         if (expr is null) {
225             throw new Exception("expr");
226         }
227 
228         if (lexer.token() == Token.LITERAL_CHARS) {
229             if (cast(SQLIdentifierExpr)(expr) !is null) {
230                 SQLIdentifierExpr identExpr = cast(SQLIdentifierExpr) expr;
231                 string ident = identExpr.getName();
232 
233                 if (equalsIgnoreCase(ident, "x")) {
234                     string charValue = lexer.stringVal();
235                     lexer.nextToken();
236                     expr = new SQLHexExpr(charValue);
237 
238                     return primaryRest(expr);
239 //                } else if (equalsIgnoreCase(ident, "b")) {
240 //                    string charValue = lexer.stringVal();
241 //                    lexer.nextToken();
242 //                    expr = new SQLBinaryExpr(charValue);
243 //
244 //                    return primaryRest(expr);
245                 } else if (ident.startsWith("_")) {
246                     string charValue = lexer.stringVal();
247                     lexer.nextToken();
248 
249                     MySqlCharExpr mysqlCharExpr = new MySqlCharExpr(charValue);
250                     mysqlCharExpr.setCharset(identExpr.getName());
251                     if (lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
252                         lexer.nextToken();
253 
254                         string collate = lexer.stringVal();
255                         mysqlCharExpr.setCollate(collate);
256                         accept(Token.IDENTIFIER);
257                     }
258 
259                     expr = mysqlCharExpr;
260 
261                     return primaryRest(expr);
262                 }
263             } else if (cast(SQLCharExpr)(expr) !is null) {
264                 string text2 = (cast(SQLCharExpr) expr).getText.value();
265                 do {
266                     string chars = lexer.stringVal();
267                     text2 ~= chars;
268                     lexer.nextToken();
269                 } while (lexer.token() == Token.LITERAL_CHARS || lexer.token() == Token.LITERAL_ALIAS);
270                 expr = new SQLCharExpr(text2);
271             } else if (cast(SQLVariantRefExpr)(expr) !is null) {
272                 SQLMethodInvokeExpr concat = new SQLMethodInvokeExpr("CONCAT");
273                 concat.addArgument(expr);
274                 concat.addArgument(this.primary());
275                 expr = concat;
276 
277                 return primaryRest(expr);
278             }
279         } else if (lexer.token() == Token.IDENTIFIER) {
280             if (cast(SQLHexExpr)(expr) !is null) {
281                 if ("USING".equalsIgnoreCase(lexer.stringVal())) {
282                     lexer.nextToken();
283                     if (lexer.token() != Token.IDENTIFIER) {
284                         throw new ParserException("syntax error, illegal hex. " ~ lexer.info());
285                     }
286                     string charSet = lexer.stringVal();
287                     lexer.nextToken();
288                     expr.getAttributes().put("USING", new String(charSet));
289 
290                     return primaryRest(expr);
291                 }
292             } else if (lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
293                 lexer.nextToken();
294 
295                 if (lexer.token() == Token.EQ) {
296                     lexer.nextToken();
297                 }
298 
299                 if (lexer.token() != Token.IDENTIFIER
300                         && lexer.token() != Token.LITERAL_CHARS) {
301                     throw new ParserException("syntax error. " ~ lexer.info());
302                 }
303 
304                 string collate = lexer.stringVal();
305                 lexer.nextToken();
306 
307                 SQLBinaryOpExpr binaryExpr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.COLLATE,
308                                                                  new SQLIdentifierExpr(collate), DBType.MYSQL.name);
309 
310                 expr = binaryExpr;
311 
312                 return primaryRest(expr);
313             } else if (cast(SQLVariantRefExpr)(expr) !is null) {
314                 if (lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
315                     lexer.nextToken();
316 
317                     if (lexer.token() != Token.IDENTIFIER
318                             && lexer.token() != Token.LITERAL_CHARS) {
319                         throw new ParserException("syntax error. " ~ lexer.info());
320                     }
321 
322                     string collate = lexer.stringVal();
323                     lexer.nextToken();
324 
325                     expr.putAttribute("COLLATE", new String(collate));
326 
327                     return primaryRest(expr);
328                 }
329             }
330         }
331 
332 //        if (lexer.token() == Token.LPAREN && cast(SQLIdentifierExpr)(expr) !is null) {
333 //            SQLIdentifierExpr identExpr = cast(SQLIdentifierExpr) expr;
334 //            string ident = identExpr.getName();
335 //
336 //            if ("POSITION".equalsIgnoreCase(ident)) {
337 //                return parsePosition();
338 //            }
339 //        }
340 
341         if (lexer.token() == Token.VARIANT && "@" == lexer.stringVal()) {
342             return userNameRest(expr);
343         }
344 
345         if (lexer.token() == Token.ERROR) {
346             throw new ParserException("syntax error. " ~ lexer.info());
347         }
348 
349         return super.primaryRest(expr);
350     }
351 
352     public SQLName userName() {
353         SQLName name = this.name();
354         if (lexer.token() == Token.LPAREN && name.hashCode64() == FnvHash.Constants.CURRENT_USER) {
355             lexer.nextToken();
356             accept(Token.RPAREN);
357             return name;
358         }
359 
360         return cast(SQLName) userNameRest(name);
361     }
362 
363     private SQLExpr userNameRest(SQLExpr expr) {
364         if (lexer.token() != Token.VARIANT || !lexer.stringVal().startsWith("@")) {
365             return expr;
366         }
367 
368         MySqlUserName userName = new MySqlUserName();
369         if (cast(SQLCharExpr)(expr) !is null) {
370             userName.setUserName((cast(SQLCharExpr) expr).toString());
371         } else {
372             userName.setUserName((cast(SQLIdentifierExpr) expr).getName());
373         }
374 
375 
376         string strVal = lexer.stringVal();
377         lexer.nextToken();
378 
379         if (strVal.length > 1) {
380             userName.setHost(strVal.substring(1));
381             return userName;
382         }
383 
384         if (lexer.token() == Token.LITERAL_CHARS) {
385             userName.setHost("'" ~ lexer.stringVal() ~ "'");
386         } else {
387             userName.setHost(lexer.stringVal());
388         }
389         lexer.nextToken();
390 
391         if (lexer.token() == Token.IDENTIFIED) {
392             Lexer.SavePoint mark = lexer.mark();
393 
394             lexer.nextToken();
395             if (lexer.token() == Token.BY) {
396                 lexer.nextToken();
397                 if (lexer.identifierEquals(FnvHash.Constants.PASSWORD)) {
398                     lexer.reset(mark);
399                 } else {
400                     userName.setIdentifiedBy(lexer.stringVal());
401                     lexer.nextToken();
402                 }
403             } else {
404                 lexer.reset(mark);
405             }
406         }
407 
408         return userName;
409     }
410 
411     override protected SQLExpr parsePosition() {
412 
413         SQLExpr subStr = this.primary();
414         accept(Token.IN);
415         SQLExpr str = this.expr();
416         accept(Token.RPAREN);
417 
418         SQLMethodInvokeExpr locate = new SQLMethodInvokeExpr("LOCATE");
419         locate.addParameter(subStr);
420         locate.addParameter(str);
421 
422         return primaryRest(locate);
423     }
424 
425     override protected SQLExpr parseExtract() {
426         SQLExpr _expr;
427         if (lexer.token() != Token.IDENTIFIER) {
428             throw new ParserException("syntax error. " ~ lexer.info());
429         }
430 
431         string unitVal = lexer.stringVal();
432         SQLIntervalUnit unit = SQLIntervalUnit(toUpper(unitVal));
433         lexer.nextToken();
434 
435         accept(Token.FROM);
436 
437         SQLExpr value = expr();
438 
439         MySqlExtractExpr extract = new MySqlExtractExpr();
440         extract.setValue(value);
441         extract.setUnit(unit);
442         accept(Token.RPAREN);
443 
444         _expr = extract;
445 
446         return primaryRest(_expr);
447     }
448 
449     override protected SQLExpr parseMatch() {
450 
451         MySqlMatchAgainstExpr matchAgainstExpr = new MySqlMatchAgainstExpr();
452 
453         if (lexer.token() == Token.RPAREN) {
454             lexer.nextToken();
455         } else {
456             exprList(matchAgainstExpr.getColumns(), matchAgainstExpr);
457             accept(Token.RPAREN);
458         }
459 
460         acceptIdentifier("AGAINST");
461 
462         accept(Token.LPAREN);
463         SQLExpr against = primary();
464         matchAgainstExpr.setAgainst(against);
465 
466         if (lexer.token() == Token.IN) {
467             lexer.nextToken();
468             if (lexer.identifierEquals(FnvHash.Constants.NATURAL)) {
469                 lexer.nextToken();
470                 acceptIdentifier("LANGUAGE");
471                 acceptIdentifier("MODE");
472                 if (lexer.token() == Token.WITH) {
473                     lexer.nextToken();
474                     acceptIdentifier("QUERY");
475                     acceptIdentifier("EXPANSION");
476                     matchAgainstExpr.setSearchModifier(MySqlMatchAgainstExpr.SearchModifier.IN_NATURAL_LANGUAGE_MODE_WITH_QUERY_EXPANSION);
477                 } else {
478                     matchAgainstExpr.setSearchModifier(MySqlMatchAgainstExpr.SearchModifier.IN_NATURAL_LANGUAGE_MODE);
479                 }
480             } else if (lexer.identifierEquals(FnvHash.Constants.BOOLEAN)) {
481                 lexer.nextToken();
482                 acceptIdentifier("MODE");
483                 matchAgainstExpr.setSearchModifier(MySqlMatchAgainstExpr.SearchModifier.IN_BOOLEAN_MODE);
484             } else {
485                 throw new ParserException("syntax error. " ~ lexer.info());
486             }
487         } else if (lexer.token() == Token.WITH) {
488             throw new ParserException("TODO. " ~ lexer.info());
489         }
490 
491         accept(Token.RPAREN);
492 
493         return primaryRest(matchAgainstExpr);
494     }
495 
496     override public SQLSelectParser createSelectParser() {
497         return new MySqlSelectParser(this);
498     }
499 
500     override protected SQLExpr parseInterval() {
501         accept(Token.INTERVAL);
502 
503         if (lexer.token() == Token.LPAREN) {
504             lexer.nextToken();
505 
506             SQLMethodInvokeExpr methodInvokeExpr = new SQLMethodInvokeExpr("INTERVAL");
507             if (lexer.token() != Token.RPAREN) {
508                 exprList(methodInvokeExpr.getParameters(), methodInvokeExpr);
509             }
510 
511             accept(Token.RPAREN);
512             
513             // 
514             
515             if (methodInvokeExpr.getParameters().size() == 1 // 
516                     && lexer.token() == Token.IDENTIFIER) {
517                 SQLExpr value = methodInvokeExpr.getParameters().get(0);
518                 string unit = lexer.stringVal();
519                 lexer.nextToken();
520                 
521                 SQLIntervalExpr intervalExpr = new SQLIntervalExpr();
522                 intervalExpr.setValue(value);
523                 intervalExpr.setUnit(SQLIntervalUnit(toUpper(unit)));
524                 return intervalExpr;
525             } else {
526                 return primaryRest(methodInvokeExpr);
527             }
528         } else {
529             SQLExpr value = expr();
530 
531             if (lexer.token() != Token.IDENTIFIER) {
532                 throw new ParserException("Syntax error. " ~ lexer.info());
533             }
534 
535             string unit = lexer.stringVal();
536             lexer.nextToken();
537 
538             SQLIntervalExpr intervalExpr = new SQLIntervalExpr();
539             intervalExpr.setValue(value);
540             intervalExpr.setUnit(SQLIntervalUnit(toUpper(unit)));
541 
542             return intervalExpr;
543         }
544     }
545 
546     override public SQLColumnDefinition parseColumn() {
547         SQLColumnDefinition column = new SQLColumnDefinition();
548         column.setDbType(dbType);
549         column.setName(name());
550         column.setDataType(parseDataType());
551 
552         return parseColumnRest(column);
553     }
554 
555     override public SQLColumnDefinition parseColumnRest(SQLColumnDefinition column) {
556         if (lexer.token() == Token.ON) {
557             lexer.nextToken();
558             accept(Token.UPDATE);
559             SQLExpr expr = this.expr();
560             column.setOnUpdate(expr);
561         }
562 
563         if (lexer.identifierEquals(FnvHash.Constants.CHARACTER)) {
564             lexer.nextToken();
565             accept(Token.SET);
566             MySqlCharExpr charSetCollateExpr=new MySqlCharExpr();
567             charSetCollateExpr.setCharset(lexer.stringVal());
568             lexer.nextToken();
569             if (lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
570                 lexer.nextToken();
571                 charSetCollateExpr.setCollate(lexer.stringVal());
572                 lexer.nextToken();
573             }
574             column.setCharsetExpr(charSetCollateExpr);
575             return parseColumnRest(column);
576         }
577 
578         if (lexer.identifierEquals(FnvHash.Constants.CHARSET)) {
579             lexer.nextToken();
580             MySqlCharExpr charSetCollateExpr=new MySqlCharExpr();
581             charSetCollateExpr.setCharset(lexer.stringVal());
582             lexer.nextToken();
583             if (lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
584                 lexer.nextToken();
585                 charSetCollateExpr.setCollate(lexer.stringVal());
586                 lexer.nextToken();
587             }
588             column.setCharsetExpr(charSetCollateExpr);
589             return parseColumnRest(column);
590         }
591         if (lexer.identifierEquals(FnvHash.Constants.AUTO_INCREMENT)) {
592             lexer.nextToken();
593             column.setAutoIncrement(true);
594             return parseColumnRest(column);
595         }
596 
597         if (lexer.identifierEquals(FnvHash.Constants.PRECISION)
598                 && column.getDataType().nameHashCode64() ==FnvHash.Constants.DOUBLE) {
599             lexer.nextToken();
600         }
601 
602         if (lexer.token() == Token.PARTITION) {
603             throw new ParserException("syntax error " ~ lexer.info());
604         }
605 
606         if (lexer.identifierEquals(FnvHash.Constants.STORAGE)) {
607             lexer.nextToken();
608             SQLExpr expr = expr();
609             column.setStorage(expr);
610         }
611         
612         if (lexer.token() == Token.AS) {
613             lexer.nextToken();
614             accept(Token.LPAREN);
615             SQLExpr expr = expr();
616             column.setAsExpr(expr);
617             accept(Token.RPAREN);
618         }
619         
620         if (lexer.identifierEquals(FnvHash.Constants.STORED)) {
621             lexer.nextToken();
622             column.setSorted(true);
623         }
624         
625         if (lexer.identifierEquals(FnvHash.Constants.VIRTUAL)) {
626             lexer.nextToken();
627             column.setVirtual(true);
628         }
629 
630         super.parseColumnRest(column);
631 
632         return column;
633     }
634 
635     override protected SQLDataType parseDataTypeRest(SQLDataType dataType) {
636         super.parseDataTypeRest(dataType);
637 
638         for (;;) {
639             if (lexer.identifierEquals(FnvHash.Constants.UNSIGNED)) {
640                 lexer.nextToken();
641                 (cast(SQLDataTypeImpl) dataType).setUnsigned(true);
642             } else if (lexer.identifierEquals(FnvHash.Constants.ZEROFILL)) {
643                 lexer.nextToken();
644                 (cast(SQLDataTypeImpl) dataType).setZerofill(true);
645             } else {
646                 break;
647             }
648         }
649 
650         return dataType;
651     }
652 
653     override public SQLAssignItem parseAssignItem() {
654         SQLAssignItem item = new SQLAssignItem();
655 
656         SQLExpr var = primary();
657 
658         string ident = null;
659         long identHash = 0;
660         if (cast(SQLIdentifierExpr)(var) !is null) {
661             SQLIdentifierExpr identExpr = cast(SQLIdentifierExpr) var;
662             ident = identExpr.getName();
663             identHash = identExpr.hashCode64();
664 
665             if (identHash == FnvHash.Constants.GLOBAL) {
666                 ident = lexer.stringVal();
667                 lexer.nextToken();
668                 var = new SQLVariantRefExpr(ident, true);
669             } else if (identHash == FnvHash.Constants.SESSION) {
670                 ident = lexer.stringVal();
671                 lexer.nextToken();
672                 var = new SQLVariantRefExpr(ident, false, true);
673             } else {
674                 var = new SQLVariantRefExpr(ident);
675             }
676         }
677 
678         if (identHash == FnvHash.Constants.NAMES) {
679             string charset = lexer.stringVal();
680 
681             SQLExpr varExpr = null;
682             bool chars = false;
683              Token token = lexer.token();
684             if (token == Token.IDENTIFIER) {
685                 lexer.nextToken();
686             } else if (token == Token.DEFAULT) {
687                 charset = "DEFAULT";
688                 lexer.nextToken();
689             } else if (token == Token.QUES) {
690                 varExpr = new SQLVariantRefExpr("?");
691                 lexer.nextToken();
692             } else {
693                 chars = true;
694                 accept(Token.LITERAL_CHARS);
695             }
696 
697             if (lexer.identifierEquals(FnvHash.Constants.COLLATE)) {
698                 MySqlCharExpr charsetExpr = new MySqlCharExpr(charset);
699                 lexer.nextToken();
700 
701                 string collate = lexer.stringVal();
702                 lexer.nextToken();
703                 charsetExpr.setCollate(collate);
704 
705                 item.setValue(charsetExpr);
706             } else {
707                 if (varExpr !is null) {
708                     item.setValue(varExpr);
709                 } else {
710                     item.setValue(chars
711                             ? new SQLCharExpr(charset)
712                             : new SQLIdentifierExpr(charset)
713                     );
714                 }
715             }
716 
717             item.setTarget(var);
718             return item;
719         } else if (identHash == FnvHash.Constants.CHARACTER) {
720             var = new SQLIdentifierExpr("CHARACTER SET");
721             accept(Token.SET);
722             if (lexer.token() == Token.EQ) {
723                 lexer.nextToken();
724             }
725         } else {
726             if (lexer.token() == Token.COLONEQ) {
727                 lexer.nextToken();
728             } else {
729                 accept(Token.EQ);
730             }
731         }
732 
733         if (lexer.token() == Token.ON) {
734             lexer.nextToken();
735             item.setValue(new SQLIdentifierExpr("ON"));
736         } else {
737             item.setValue(this.expr());
738         }
739 
740         item.setTarget(var);
741         return item;
742     }
743 
744     override public SQLName nameRest(SQLName name) {
745         if (lexer.token() == Token.VARIANT && "@" == lexer.stringVal()) {
746             lexer.nextToken();
747             MySqlUserName userName = new MySqlUserName();
748             userName.setUserName((cast(SQLIdentifierExpr) name).getName());
749 
750             if (lexer.token() == Token.LITERAL_CHARS) {
751                 userName.setHost("'" ~ lexer.stringVal() ~ "'");
752             } else {
753                 userName.setHost(lexer.stringVal());
754             }
755             lexer.nextToken();
756 
757             if (lexer.token() == Token.IDENTIFIED) {
758                 lexer.nextToken();
759                 accept(Token.BY);
760                 userName.setIdentifiedBy(lexer.stringVal());
761                 lexer.nextToken();
762             }
763 
764             return userName;
765         }
766         return super.nameRest(name);
767     }
768 
769     override
770     public MySqlPrimaryKey parsePrimaryKey() {
771         accept(Token.PRIMARY);
772         accept(Token.KEY);
773 
774         MySqlPrimaryKey primaryKey = new MySqlPrimaryKey();
775 
776         if (lexer.identifierEquals(FnvHash.Constants.USING)) {
777             lexer.nextToken();
778             primaryKey.setIndexType(lexer.stringVal());
779             lexer.nextToken();
780         }
781 
782         if (lexer.token() != Token.LPAREN) {
783             SQLName name = this.name();
784             primaryKey.setName(name);
785         }
786 
787         accept(Token.LPAREN);
788         for (;;) {
789             SQLExpr expr;
790             if (lexer.token() == Token.LITERAL_ALIAS) {
791                 expr = this.name();
792             } else {
793                 expr = this.expr();
794             }
795             primaryKey.addColumn(expr);
796             if (!(lexer.token() == (Token.COMMA))) {
797                 break;
798             } else {
799                 lexer.nextToken();
800             }
801         }
802         accept(Token.RPAREN);
803 
804         if (lexer.identifierEquals(FnvHash.Constants.USING)) {
805             lexer.nextToken();
806             primaryKey.setIndexType(lexer.stringVal());
807             lexer.nextToken();
808         }
809 
810         return primaryKey;
811     }
812 
813     override public MySqlUnique parseUnique() {
814         accept(Token.UNIQUE);
815 
816         if (lexer.token() == Token.KEY) {
817             lexer.nextToken();
818         }
819 
820         if (lexer.token() == Token.INDEX) {
821             lexer.nextToken();
822         }
823 
824         MySqlUnique unique = new MySqlUnique();
825 
826         if (lexer.token() != Token.LPAREN) {
827             SQLName indexName = name();
828             unique.setName(indexName);
829         }
830         
831         //5.5语法 USING BTREE 放在index 名字后
832         if (lexer.identifierEquals(FnvHash.Constants.USING)) {
833             lexer.nextToken();
834             unique.setIndexType(lexer.stringVal());
835             lexer.nextToken();
836         }
837 
838         accept(Token.LPAREN);
839         for (;;) {
840             SQLExpr column = this.expr();
841             if (lexer.token() == Token.ASC) {
842                 column = new MySqlOrderingExpr(column, SQLOrderingSpecification.ASC);
843                 lexer.nextToken();
844             } else if (lexer.token() == Token.DESC) {
845                 column = new MySqlOrderingExpr(column, SQLOrderingSpecification.DESC);
846                 lexer.nextToken();
847             }
848             unique.addColumn(column);
849             if (!(lexer.token() == (Token.COMMA))) {
850                 break;
851             } else {
852                 lexer.nextToken();
853             }
854         }
855         accept(Token.RPAREN);
856 
857         if (lexer.identifierEquals(FnvHash.Constants.USING)) {
858             lexer.nextToken();
859             unique.setIndexType(lexer.stringVal());
860             lexer.nextToken();
861         }
862 
863         if (lexer.identifierEquals(FnvHash.Constants.KEY_BLOCK_SIZE)) {
864             lexer.nextToken();
865             if (lexer.token() == Token.EQ) {
866                 lexer.nextToken();
867             }
868             SQLExpr value = this.primary();
869             unique.setKeyBlockSize(value);
870         }
871 
872         return unique;
873     }
874 
875     override public MysqlForeignKey parseForeignKey() {
876         accept(Token.FOREIGN);
877         accept(Token.KEY);
878 
879         MysqlForeignKey fk = new MysqlForeignKey();
880 
881         if (lexer.token() != Token.LPAREN) {
882             SQLName indexName = name();
883             fk.setIndexName(indexName);
884         }
885 
886         accept(Token.LPAREN);
887         this.names(fk.getReferencingColumns(), fk);
888         accept(Token.RPAREN);
889 
890         accept(Token.REFERENCES);
891 
892         fk.setReferencedTableName(this.name());
893 
894         accept(Token.LPAREN);
895         this.names(fk.getReferencedColumns());
896         accept(Token.RPAREN);
897 
898         if (lexer.identifierEquals(FnvHash.Constants.MATCH)) {
899             lexer.nextToken();
900             if (lexer.identifierEquals("FULL") || lexer.token() == Token.FULL) {
901                 fk.setReferenceMatch(SQLForeignKeyImpl.Match.FULL);
902                 lexer.nextToken();
903             } else if (lexer.identifierEquals(FnvHash.Constants.PARTIAL)) {
904                 fk.setReferenceMatch(SQLForeignKeyImpl.Match.PARTIAL);
905                 lexer.nextToken();
906             } else if (lexer.identifierEquals(FnvHash.Constants.SIMPLE)) {
907                 fk.setReferenceMatch(SQLForeignKeyImpl.Match.SIMPLE);
908                 lexer.nextToken();
909             } else {
910                 throw new ParserException("TODO : " ~ lexer.info());
911             }
912         }
913 
914         while (lexer.token() == Token.ON) {
915             lexer.nextToken();
916             
917             if (lexer.token() == Token.DELETE) {
918                 lexer.nextToken();
919                 
920                 SQLForeignKeyImpl.Option option = parseReferenceOption();
921                 fk.setOnDelete(option);
922             } else if (lexer.token() == Token.UPDATE) {
923                 lexer.nextToken();
924                 
925                 SQLForeignKeyImpl.Option option = parseReferenceOption();
926                 fk.setOnUpdate(option);
927             } else {
928                 throw new ParserException("syntax error, expect DELETE or UPDATE, actual " ~ lexer.token() ~ " "
929                                           ~ lexer.info());
930             }
931         }
932         return fk;
933     }
934 
935     override protected SQLAggregateExpr parseAggregateExprRest(SQLAggregateExpr aggregateExpr) {
936         if (lexer.token() == Token.ORDER) {
937             SQLOrderBy orderBy = this.parseOrderBy();
938             aggregateExpr.putAttribute("ORDER BY", orderBy);
939         }
940         if (lexer.identifierEquals(FnvHash.Constants.SEPARATOR)) {
941             lexer.nextToken();
942 
943             SQLExpr seperator = this.primary();
944             seperator.setParent(aggregateExpr);
945 
946             aggregateExpr.putAttribute("SEPARATOR", cast(Object)seperator);
947         }
948         return aggregateExpr;
949     }
950 
951     public MySqlOrderingExpr parseSelectGroupByItem() {
952         MySqlOrderingExpr item = new MySqlOrderingExpr();
953 
954         item.setExpr(expr());
955 
956         if (lexer.token() == Token.ASC) {
957             lexer.nextToken();
958             item.setType(SQLOrderingSpecification.ASC);
959         } else if (lexer.token() == Token.DESC) {
960             lexer.nextToken();
961             item.setType(SQLOrderingSpecification.DESC);
962         }
963 
964         return item;
965     }
966     
967     override public SQLPartition parsePartition() {
968         accept(Token.PARTITION);
969 
970         SQLPartition partitionDef = new SQLPartition();
971 
972         partitionDef.setName(this.name());
973 
974         SQLPartitionValue values = this.parsePartitionValues();
975         if (values !is null) {
976             partitionDef.setValues(values);
977         }
978 
979         for (;;) {
980             bool storage = false;
981             if (lexer.identifierEquals(FnvHash.Constants.DATA)) {
982                 lexer.nextToken();
983                 acceptIdentifier("DIRECTORY");
984                 if (lexer.token() == Token.EQ) {
985                     lexer.nextToken();
986                 }
987                 partitionDef.setDataDirectory(this.expr());
988             } else if (lexer.token() == Token.TABLESPACE) {
989                 lexer.nextToken();
990                 if (lexer.token() == Token.EQ) {
991                     lexer.nextToken();
992                 }
993                 SQLName tableSpace = this.name();
994                 partitionDef.setTablespace(tableSpace);
995             } else if (lexer.token() == Token.INDEX) {
996                 lexer.nextToken();
997                 acceptIdentifier("DIRECTORY");
998                 if (lexer.token() == Token.EQ) {
999                     lexer.nextToken();
1000                 }
1001                 partitionDef.setIndexDirectory(this.expr());
1002             } else if (lexer.identifierEquals(FnvHash.Constants.MAX_ROWS)) {
1003                 lexer.nextToken();
1004                 if (lexer.token() == Token.EQ) {
1005                     lexer.nextToken();
1006                 }
1007                 SQLExpr maxRows = this.primary();
1008                 partitionDef.setMaxRows(maxRows);
1009             } else if (lexer.identifierEquals(FnvHash.Constants.MIN_ROWS)) {
1010                 lexer.nextToken();
1011                 if (lexer.token() == Token.EQ) {
1012                     lexer.nextToken();
1013                 }
1014                 SQLExpr minRows = this.primary();
1015                 partitionDef.setMaxRows(minRows);
1016             } else if (lexer.identifierEquals(FnvHash.Constants.ENGINE)  //
1017                        ) {
1018                 storage = (lexer.token() == Token.STORAGE || lexer.identifierEquals(FnvHash.Constants.STORAGE));
1019                 if (storage) {
1020                     lexer.nextToken();
1021                 }
1022                 acceptIdentifier("ENGINE");
1023 
1024                 if (lexer.token() == Token.EQ) {
1025                     lexer.nextToken();
1026                 }
1027 
1028                 SQLName engine = this.name();
1029                 partitionDef.setEngine(engine);
1030             } else if (lexer.token() == Token.COMMENT) {
1031                 lexer.nextToken();
1032                 if (lexer.token() == Token.EQ) {
1033                     lexer.nextToken();
1034                 }
1035                 SQLExpr comment = this.primary();
1036                 partitionDef.setComment(comment);
1037             } else {
1038                 break;
1039             }
1040         }
1041         
1042         if (lexer.token() == Token.LPAREN) {
1043             lexer.nextToken();
1044             
1045             for (;;) {
1046                 acceptIdentifier("SUBPARTITION");
1047                 
1048                 SQLName subPartitionName = this.name();
1049                 SQLSubPartition subPartition = new SQLSubPartition();
1050                 subPartition.setName(subPartitionName);
1051                 
1052                 partitionDef.addSubPartition(subPartition);
1053                 
1054                 if (lexer.token() == Token.COMMA) {
1055                     lexer.nextToken();
1056                     continue;
1057                 }
1058                 break;
1059             }
1060             
1061             accept(Token.RPAREN);
1062         }
1063         return partitionDef;
1064     }
1065 
1066     override protected SQLExpr parseAliasExpr(string alias_p) {
1067         string chars = alias_p.substring(1, cast(int)(alias_p.length - 1));
1068         return new SQLCharExpr(chars);
1069     }
1070 }