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.parser.SQLSelectParser;
17 
18 import hunt.collection;
19 
20 import hunt.sql.ast;
21 import hunt.sql.ast.expr;
22 import hunt.sql.ast.statement;
23 // import hunt.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock;
24 import hunt.sql.dialect.mysql.ast.expr.MySqlOrderingExpr;
25 import hunt.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
26 import hunt.sql.util.FnvHash;
27 import hunt.sql.util.DBType;
28 import hunt.sql.parser.SQLParser;
29 import hunt.sql.parser.SQLExprParser;
30 import hunt.sql.parser.SQLSelectListCache;
31 import hunt.sql.parser.Lexer;
32 import hunt.sql.parser.SQLExprParser;
33 import hunt.sql.parser.Token;
34 import hunt.Float;
35 import hunt.sql.parser.ParserException;
36 import hunt.String;
37 import hunt.text;
38 import hunt.sql.ast.SQLCommentHint;
39 
40 public class SQLSelectParser : SQLParser {
41     protected SQLExprParser      exprParser;
42     protected SQLSelectListCache selectListCache;
43 
44     public this(string sql){
45         super(sql);
46     }
47 
48     public this(Lexer lexer){
49         super(lexer);
50     }
51 
52     public this(SQLExprParser exprParser){
53         this(exprParser, null);
54     }
55 
56     public this(SQLExprParser exprParser, SQLSelectListCache selectListCache){
57         super(exprParser.getLexer(), exprParser.getDbType());
58         this.exprParser = exprParser;
59         this.selectListCache = selectListCache;
60     }
61 
62     public SQLSelect select() {
63         SQLSelect select = new SQLSelect();
64 
65         if (lexer.token == Token.WITH) {
66             SQLWithSubqueryClause _with = this.parseWith();
67             select.setWithSubQuery(_with);
68         }
69 
70         SQLSelectQuery query = query();
71         select.setQuery(query);
72 
73         SQLOrderBy orderBy = this.parseOrderBy();
74 
75         if (cast(SQLSelectQueryBlock)(query) !is null) {
76             SQLSelectQueryBlock queryBlock = cast(SQLSelectQueryBlock) query;
77 
78             if (queryBlock.getOrderBy() is null) {
79                 queryBlock.setOrderBy(orderBy);
80             } else {
81                 select.setOrderBy(orderBy);
82             }
83 
84             if (orderBy !is null) {
85                 parseFetchClause(queryBlock);
86             }
87         } else {
88             select.setOrderBy(orderBy);
89         }
90 
91         while (lexer.token == Token.HINT) {
92             this.exprParser.parseHints!(SQLHint)((select.getHints()));
93         }
94 
95         return select;
96     }
97 
98     protected SQLUnionQuery createSQLUnionQuery() {
99         SQLUnionQuery _union = new SQLUnionQuery();
100         _union.setDbType(getDbType());
101         return _union;
102     }
103 
104     public SQLUnionQuery unionRest(SQLUnionQuery _union) {
105         if (lexer.token == Token.ORDER) {
106             SQLOrderBy orderBy = this.exprParser.parseOrderBy();
107             _union.setOrderBy(orderBy);
108             return unionRest(_union);
109         }
110         return _union;
111     }
112 
113     public SQLSelectQuery queryRest(SQLSelectQuery selectQuery) {
114         return queryRest(selectQuery, true);
115     }
116 
117     public SQLSelectQuery queryRest(SQLSelectQuery selectQuery, bool acceptUnion) {
118         if (!acceptUnion) {
119             return selectQuery;
120         }
121 
122         if (lexer.token == Token.UNION) {
123             do {
124                 lexer.nextToken();
125 
126                 SQLUnionQuery _union = createSQLUnionQuery();
127                 if (_union.getLeft() is null) {
128                     _union.setLeft(selectQuery);
129                 }
130 
131                 bool paren = lexer.token == Token.LPAREN;
132 
133                 if (lexer.token == Token.ALL) {
134                     _union.setOperator(SQLUnionOperator.UNION_ALL);
135                     lexer.nextToken();
136                 } else if (lexer.token == Token.DISTINCT) {
137                     _union.setOperator(SQLUnionOperator.DISTINCT);
138                     lexer.nextToken();
139                 }
140                 SQLSelectQuery right = this.query(paren ? null : _union, false);
141                 _union.setRight(right);
142 
143                 if (!paren) {
144                     if (cast(SQLSelectQueryBlock)(right) !is null) {
145                         SQLSelectQueryBlock rightQuery = cast(SQLSelectQueryBlock) right;
146                         SQLOrderBy orderBy = rightQuery.getOrderBy();
147                         SQLLimit limit = rightQuery.getLimit();
148                         if (orderBy !is null && limit is null) {
149                             _union.setOrderBy(orderBy);
150                             rightQuery.setOrderBy(null);
151                         }
152                     } else if (cast(SQLUnionQuery)(right) !is null) {
153                         SQLUnionQuery rightUnion = cast(SQLUnionQuery) right;
154                         if (rightUnion.getOrderBy() !is null) {
155                             _union.setOrderBy(rightUnion.getOrderBy());
156                             rightUnion.setOrderBy(null);
157                         }
158                     }
159                 }
160 
161                 _union = unionRest(_union);
162 
163                 selectQuery = _union;
164 
165             } while (lexer.token() == Token.UNION);
166 
167             selectQuery = queryRest(selectQuery, true);
168 
169             return selectQuery;
170         }
171 
172         if (lexer.token == Token.EXCEPT) {
173             lexer.nextToken();
174 
175             SQLUnionQuery _union = new SQLUnionQuery();
176             _union.setLeft(selectQuery);
177 
178             _union.setOperator(SQLUnionOperator.EXCEPT);
179 
180             SQLSelectQuery right = this.query(_union, false);
181             _union.setRight(right);
182 
183             return queryRest(_union, true);
184         }
185 
186         if (lexer.token == Token.INTERSECT) {
187             lexer.nextToken();
188 
189             SQLUnionQuery _union = new SQLUnionQuery();
190             _union.setLeft(selectQuery);
191 
192             _union.setOperator(SQLUnionOperator.INTERSECT);
193 
194             SQLSelectQuery right = this.query(_union, false);
195             _union.setRight(right);
196 
197             return queryRest(_union, true);
198         }
199 
200         if (acceptUnion && lexer.token == Token.MINUS) {
201             lexer.nextToken();
202 
203             SQLUnionQuery _union = new SQLUnionQuery();
204             _union.setLeft(selectQuery);
205 
206             _union.setOperator(SQLUnionOperator.MINUS);
207 
208             SQLSelectQuery right = this.query(_union, false);
209             _union.setRight(right);
210 
211             return queryRest(_union, true);
212         }
213 
214         return selectQuery;
215     }
216 
217     public SQLSelectQuery query() {
218         return query(null, true);
219     }
220 
221     public SQLSelectQuery query(SQLObject parent) {
222         return query(parent, true);
223     }
224 
225     public SQLSelectQuery query(SQLObject parent, bool acceptUnion) {
226         if (lexer.token == Token.LPAREN) {
227             lexer.nextToken();
228 
229             SQLSelectQuery select = query();
230             accept(Token.RPAREN);
231 
232             return queryRest(select, acceptUnion);
233         }
234 
235         SQLSelectQueryBlock queryBlock = new SQLSelectQueryBlock();
236 
237         if (lexer.hasComment() && lexer.isKeepComments()) {
238             queryBlock.addBeforeComment(lexer.readAndResetComments());
239         }
240 
241         accept(Token.SELECT);
242 
243         if (lexer.token() == Token.HINT) {
244             this.exprParser.parseHints!(SQLCommentHint)((queryBlock.getHints()));
245         }
246 
247         if (lexer.token == Token.COMMENT) {
248             lexer.nextToken();
249         }
250 
251         if (DBType.INFORMIX.opEquals(dbType)) {
252             if (lexer.identifierEquals(FnvHash.Constants.SKIP)) {
253                 lexer.nextToken();
254                 SQLExpr offset = this.exprParser.primary();
255                 queryBlock.setOffset(offset);
256             }
257 
258             if (lexer.identifierEquals(FnvHash.Constants.FIRST)) {
259                 lexer.nextToken();
260                 SQLExpr first = this.exprParser.primary();
261                 queryBlock.setFirst(first);
262             }
263         }
264 
265         if (lexer.token == Token.DISTINCT) {
266             queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT);
267             lexer.nextToken();
268         } else if (lexer.token == Token.UNIQUE) {
269             queryBlock.setDistionOption(SQLSetQuantifier.UNIQUE);
270             lexer.nextToken();
271         } else if (lexer.token == Token.ALL) {
272             queryBlock.setDistionOption(SQLSetQuantifier.ALL);
273             lexer.nextToken();
274         }
275 
276         parseSelectList(queryBlock);
277 
278         parseFrom(queryBlock);
279 
280         parseWhere(queryBlock);
281 
282         parseGroupBy(queryBlock);
283 
284         parseSortBy(queryBlock);
285 
286         parseFetchClause(queryBlock);
287 
288         if (lexer.token() == Token.FOR) {
289             lexer.nextToken();
290             accept(Token.UPDATE);
291 
292             queryBlock.setForUpdate(true);
293 
294             if (lexer.identifierEquals(FnvHash.Constants.NO_WAIT) || lexer.identifierEquals(FnvHash.Constants.NOWAIT)) {
295                 lexer.nextToken();
296                 queryBlock.setNoWait(true);
297             } else if (lexer.identifierEquals(FnvHash.Constants.WAIT)) {
298                 lexer.nextToken();
299                 SQLExpr waitTime = this.exprParser.primary();
300                 queryBlock.setWaitTime(waitTime);
301             }
302         }
303 
304         return queryRest(queryBlock, acceptUnion);
305     }
306 
307     protected void parseSortBy(SQLSelectQueryBlock queryBlock) {
308 
309     }
310 
311     protected void withSubquery(SQLSelect s_select) {
312         if (lexer.token == Token.WITH) {
313             lexer.nextToken();
314 
315             SQLWithSubqueryClause withQueryClause = new SQLWithSubqueryClause();
316 
317             if (lexer.token == Token.RECURSIVE || lexer.identifierEquals(FnvHash.Constants.RECURSIVE)) {
318                 lexer.nextToken();
319                 withQueryClause.setRecursive(true);
320             }
321 
322             for (;;) {
323                 SQLWithSubqueryClause.Entry entry = new SQLWithSubqueryClause.Entry();
324                 entry.setParent(withQueryClause);
325 
326                 string _alias = this.lexer.stringVal();
327                 lexer.nextToken();
328                 entry.setAlias(_alias);
329 
330                 if (lexer.token == Token.LPAREN) {
331                     lexer.nextToken();
332                     exprParser.names(entry.getColumns());
333                     accept(Token.RPAREN);
334                 }
335 
336                 accept(Token.AS);
337                 accept(Token.LPAREN);
338                 entry.setSubQuery(select());
339                 accept(Token.RPAREN);
340 
341                 withQueryClause.addEntry(entry);
342 
343                 if (lexer.token == Token.COMMA) {
344                     lexer.nextToken();
345                     continue;
346                 }
347 
348                 break;
349             }
350 
351             s_select.setWithSubQuery(withQueryClause);
352         }
353     }
354 
355     public SQLWithSubqueryClause parseWith() {
356         accept(Token.WITH);
357 
358         SQLWithSubqueryClause withQueryClause = new SQLWithSubqueryClause();
359 
360         if (lexer.token == Token.RECURSIVE || lexer.identifierEquals(FnvHash.Constants.RECURSIVE)) {
361             lexer.nextToken();
362             withQueryClause.setRecursive(true);
363         }
364 
365         for (;;) {
366             SQLWithSubqueryClause.Entry entry = new SQLWithSubqueryClause.Entry();
367             entry.setParent(withQueryClause);
368 
369             string _alias = this.lexer.stringVal();
370             lexer.nextToken();
371             entry.setAlias(_alias);
372 
373             if (lexer.token == Token.LPAREN) {
374                 lexer.nextToken();
375                 exprParser.names(entry.getColumns());
376                 accept(Token.RPAREN);
377             }
378 
379             accept(Token.AS);
380             accept(Token.LPAREN);
381 
382             switch (lexer.token) {
383                 case Token.SELECT:
384                     entry.setSubQuery(select());
385                     break;
386                 default:
387                     break;
388             }
389 
390             accept(Token.RPAREN);
391 
392             withQueryClause.addEntry(entry);
393 
394             if (lexer.token == Token.COMMA) {
395                 lexer.nextToken();
396                 continue;
397             }
398 
399             break;
400         }
401 
402         return withQueryClause;
403     }
404 
405     public void parseWhere(SQLSelectQueryBlock queryBlock) {
406         if (lexer.token == Token.WHERE) {
407             lexer.nextTokenIdent();
408 
409             List!(string) beforeComments = null;
410             if (lexer.hasComment() && lexer.isKeepComments()) {
411                 beforeComments = lexer.readAndResetComments();
412             }
413 
414             SQLExpr where;
415 
416             if (lexer.token == Token.IDENTIFIER) {
417                 string ident = lexer.stringVal();
418                 long hash_lower = lexer.hash_lower();
419                 lexer.nextTokenEq();
420 
421                 SQLExpr identExpr;
422                 if (lexer.token == Token.LITERAL_CHARS) {
423                     string literal = lexer.stringVal;
424                     if (hash_lower == FnvHash.Constants.TIMESTAMP) {
425                         identExpr = new SQLTimestampExpr(literal);
426                         lexer.nextToken();
427                     } else if (hash_lower == FnvHash.Constants.DATE) {
428                         identExpr = new SQLDateExpr(literal);
429                         lexer.nextToken();
430                     } else if (hash_lower == FnvHash.Constants.REAL) {
431                         identExpr = new SQLRealExpr(Float.parseFloat(literal));
432                         lexer.nextToken();
433                     } else {
434                         identExpr = new SQLIdentifierExpr(ident, hash_lower);
435                     }
436                 } else {
437                     identExpr = new SQLIdentifierExpr(ident, hash_lower);
438                 }
439 
440                 if (lexer.token == Token.DOT) {
441                     identExpr = this.exprParser.primaryRest(identExpr);
442                 }
443 
444                 if (lexer.token == Token.EQ) {
445                     SQLExpr rightExp;
446 
447                     lexer.nextToken();
448 
449                     try {
450                         rightExp = this.exprParser.bitOr();
451                     } catch (ParserException e) {
452                         throw new ParserException("EOF, " ~ ident ~ "=", e);
453                     }
454 
455                     where = new SQLBinaryOpExpr(identExpr, SQLBinaryOperator.Equality, rightExp, dbType);
456                     switch (lexer.token) {
457                         case Token.BETWEEN:
458                         case Token.IS:
459                         case Token.EQ:
460                         case Token.IN:
461                         case Token.CONTAINS:
462                         case Token.BANG_TILDE_STAR:
463                         case Token.TILDE_EQ:
464                         case Token.LT:
465                         case Token.LTEQ:
466                         case Token.LTEQGT:
467                         case Token.GT:
468                         case Token.GTEQ:
469                         case Token.LTGT:
470                         case Token.BANGEQ:
471                         case Token.LIKE:
472                         case Token.NOT:
473                             where = this.exprParser.relationalRest(where);
474                             break;
475                         default:
476                             break;
477                     }
478 
479                     where = this.exprParser.andRest(where);
480                     where = this.exprParser.xorRest(where);
481                     where = this.exprParser.orRest(where);
482                 } else {
483                     identExpr = this.exprParser.primaryRest(identExpr);
484                     where = this.exprParser.exprRest(identExpr);
485                 }
486             } else {
487                 where = this.exprParser.expr();
488             }
489 //            where = this.exprParser.expr();
490 
491             if (beforeComments !is null) {
492                 where.addBeforeComment(beforeComments);
493             }
494 
495             if (lexer.hasComment() && lexer.isKeepComments() //
496                     && lexer.token != Token.INSERT // odps multi-insert
497                     ) {
498                 where.addAfterComment(lexer.readAndResetComments());
499             }
500 
501             queryBlock.setWhere(where);
502         }
503     }
504 
505     protected void parseWindow(SQLSelectQueryBlock queryBlock) {
506         if (!(lexer.identifierEquals(FnvHash.Constants.WINDOW) || lexer.token == Token.WINDOW)) {
507             return;
508         }
509 
510         lexer.nextToken();
511 
512         for (;;) {
513             SQLName name = this.exprParser.name();
514             accept(Token.AS);
515             SQLOver s_over = new SQLOver();
516             this.exprParser.over(s_over);
517             queryBlock.addWindow(new SQLWindow(name, s_over));
518 
519             if (lexer.token == Token.COMMA) {
520                 lexer.nextToken();
521                 continue;
522             }
523 
524             break;
525         }
526     }
527     
528     protected void parseGroupBy(SQLSelectQueryBlock queryBlock) {
529         if (lexer.token == (Token.GROUP)) {
530             lexer.nextTokenBy();
531             accept(Token.BY);
532 
533             SQLSelectGroupByClause groupBy = new SQLSelectGroupByClause();
534             if (lexer.identifierEquals(FnvHash.Constants.ROLLUP)) {
535                 lexer.nextToken();
536                 accept(Token.LPAREN);
537                 groupBy.setWithRollUp(true);
538             }
539             if (lexer.identifierEquals(FnvHash.Constants.CUBE)) {
540                 lexer.nextToken();
541                 accept(Token.LPAREN);
542                 groupBy.setWithCube(true);
543             }
544 
545             for (;;) {
546                 SQLExpr item = parseGroupByItem();
547                 
548                 item.setParent(groupBy);
549                 groupBy.addItem(item);
550 
551                 if (!(lexer.token == (Token.COMMA))) {
552                     break;
553                 }
554 
555                 lexer.nextToken();
556             }
557             if (groupBy.isWithRollUp() || groupBy.isWithCube()) {
558                 accept(Token.RPAREN);
559             }
560 
561             if (lexer.token == (Token.HAVING)) {
562                 lexer.nextToken();
563 
564                 SQLExpr having = this.exprParser.expr();
565                 groupBy.setHaving(having);
566             }
567             
568             if (lexer.token == Token.WITH) {
569                 lexer.nextToken();
570                 
571                 if (lexer.identifierEquals(FnvHash.Constants.CUBE)) {
572                     lexer.nextToken();
573                     groupBy.setWithCube(true);
574                 } else if(lexer.identifierEquals(FnvHash.Constants.ROLLUP)) {
575                     lexer.nextToken();
576                     groupBy.setWithRollUp(true);
577                 // } else if (lexer.identifierEquals(FnvHash.Constants.RS)
578                 //         && DBType.DB2.opEquals(dbType)) {
579                 //     lexer.nextToken();
580                 //     ((DB2SelectQueryBlock) queryBlock).setIsolation(DB2SelectQueryBlock.Isolation.RS);
581                 // } else if (lexer.identifierEquals(FnvHash.Constants.RR)
582                 //         && DBType.DB2.opEquals(dbType)) {
583                 //     lexer.nextToken();
584                 //     ((DB2SelectQueryBlock) queryBlock).setIsolation(DB2SelectQueryBlock.Isolation.RR);
585                 // } else if (lexer.identifierEquals(FnvHash.Constants.CS)
586                 //         && DBType.DB2.opEquals(dbType)) {
587                 //     lexer.nextToken();
588                 //     ((DB2SelectQueryBlock) queryBlock).setIsolation(DB2SelectQueryBlock.Isolation.CS);
589                 // } else if (lexer.identifierEquals(FnvHash.Constants.UR)
590                 //         && DBType.DB2.opEquals(dbType)) {
591                 //     lexer.nextToken();
592                 //     ((DB2SelectQueryBlock) queryBlock).setIsolation(DB2SelectQueryBlock.Isolation.UR);
593                 } else {
594                     throw new ParserException("TODO " ~ lexer.info());
595                 }
596             }
597             
598             queryBlock.setGroupBy(groupBy);
599         } else if (lexer.token == (Token.HAVING)) {
600             lexer.nextToken();
601 
602             SQLSelectGroupByClause groupBy = new SQLSelectGroupByClause();
603             groupBy.setHaving(this.exprParser.expr());
604 
605             if (lexer.token == (Token.GROUP)) {
606                 lexer.nextToken();
607                 accept(Token.BY);
608 
609                 for (;;) {
610                     SQLExpr item = parseGroupByItem();
611                     
612                     item.setParent(groupBy);
613                     groupBy.addItem(item);
614 
615                     if (!(lexer.token == (Token.COMMA))) {
616                         break;
617                     }
618 
619                     lexer.nextToken();
620                 }
621             }
622             
623             if (lexer.token == Token.WITH) {
624                 lexer.nextToken();
625                 acceptIdentifier("ROLLUP");
626 
627                 groupBy.setWithRollUp(true);
628             }
629             
630             if(DBType.MYSQL.opEquals(getDbType())
631                     && lexer.token == Token.DESC) {
632                 lexer.nextToken(); // skip
633             }
634 
635             queryBlock.setGroupBy(groupBy);
636         }
637     }
638 
639     protected SQLExpr parseGroupByItem() {
640         SQLExpr item = this.exprParser.expr();
641         
642         if(DBType.MYSQL.opEquals(getDbType())) {
643             if (lexer.token == Token.DESC) {
644                 lexer.nextToken(); // skip
645                 item =new MySqlOrderingExpr(item, SQLOrderingSpecification.DESC);
646             } else if (lexer.token == Token.ASC) {
647                 lexer.nextToken(); // skip
648                 item =new MySqlOrderingExpr(item, SQLOrderingSpecification.ASC);
649             }
650         }
651         return item;
652     }
653 
654     public void parseSelectList(SQLSelectQueryBlock queryBlock) {
655          List!(SQLSelectItem) selectList = queryBlock.getSelectList();
656         for (;;) {
657              SQLSelectItem selectItem = this.exprParser.parseSelectItem();
658             selectList.add(selectItem);
659             selectItem.setParent(queryBlock);
660 
661             if (lexer.token != Token.COMMA) {
662                 break;
663             }
664 
665             lexer.nextToken();
666         }
667     }
668 
669     public void parseFrom(SQLSelectQueryBlock queryBlock) {
670         if (lexer.token != Token.FROM) {
671             return;
672         }
673         
674         lexer.nextToken();
675         
676         queryBlock.setFrom(
677                 parseTableSource());
678     }
679 
680     public SQLTableSource parseTableSource() {
681         if (lexer.token == Token.LPAREN) {
682             lexer.nextToken();
683             SQLTableSource tableSource;
684             if (lexer.token == Token.SELECT || lexer.token == Token.WITH
685                     || lexer.token == Token.SEL) {
686                 SQLSelect select = select();
687                 accept(Token.RPAREN);
688                 SQLSelectQuery query = queryRest(select.getQuery(), true);
689                 if (cast(SQLUnionQuery)(query) !is null) {
690                     tableSource = new SQLUnionQueryTableSource(cast(SQLUnionQuery) query);
691                 } else {
692                     tableSource = new SQLSubqueryTableSource(select);
693                 }
694             } else if (lexer.token == Token.LPAREN) {
695                 tableSource = parseTableSource();
696                 accept(Token.RPAREN);
697             } else {
698                 tableSource = parseTableSource();
699                 accept(Token.RPAREN);
700             }
701 
702             if (lexer.token == Token.AS
703                     && cast(SQLValuesTableSource)(tableSource) !is null
704                     && (cast(SQLValuesTableSource) tableSource).getColumns().size() == 0)
705             {
706                 lexer.nextToken();
707 
708                 string _alias = this.tableAlias();
709                 tableSource.setAlias(_alias);
710 
711                 SQLValuesTableSource values = cast(SQLValuesTableSource) tableSource;
712                 accept(Token.LPAREN);
713                 this.exprParser.names(values.getColumns(), values);
714                 accept(Token.RPAREN);
715             }
716 
717             return parseTableSourceRest(tableSource);
718         }
719 
720         if (lexer.token() == Token.VALUES) {
721             lexer.nextToken();
722             SQLValuesTableSource tableSource = new SQLValuesTableSource();
723 
724             for (;;) {
725                 accept(Token.LPAREN);
726                 SQLListExpr listExpr = new SQLListExpr();
727                 this.exprParser.exprList(listExpr.getItems(), listExpr);
728                 accept(Token.RPAREN);
729 
730                 listExpr.setParent(tableSource);
731 
732                 tableSource.getValues().add(listExpr);
733 
734                 if (lexer.token == Token.COMMA) {
735                     lexer.nextToken();
736                     continue;
737                 }
738                 break;
739             }
740 
741             if (lexer.token == Token.RPAREN) {
742                 return tableSource;
743             }
744 
745             string _alias = this.tableAlias();
746             if (_alias !is null) {
747                 tableSource.setAlias(_alias);
748             }
749 
750             accept(Token.LPAREN);
751             this.exprParser.names(tableSource.getColumns(), tableSource);
752             accept(Token.RPAREN);
753 
754             return tableSource;
755         }
756 
757         if (lexer.token == Token.SELECT) {
758             throw new ParserException("TODO " ~ lexer.info());
759         }
760 
761         SQLExprTableSource tableReference = new SQLExprTableSource();
762 
763         parseTableSourceQueryTableExpr(tableReference);
764 
765         SQLTableSource tableSrc = parseTableSourceRest(tableReference);
766 
767         if (lexer.hasComment() && lexer.isKeepComments()) {
768             tableSrc.addAfterComment(lexer.readAndResetComments());
769         }
770 
771         return tableSrc;
772     }
773 
774     protected void parseTableSourceQueryTableExpr(SQLExprTableSource tableReference) {
775         if (lexer.token == Token.LITERAL_ALIAS || lexer.token == Token.IDENTIFIED
776             || lexer.token == Token.LITERAL_CHARS) {
777             tableReference.setExpr(this.exprParser.name());
778             return;
779         }
780 
781         tableReference.setExpr(expr());
782     }
783 
784     protected SQLTableSource primaryTableSourceRest(SQLTableSource tableSource) {
785         return tableSource;
786     }
787 
788     protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) {
789         if (tableSource.getAlias() is null || tableSource.getAlias().length == 0) {
790             Token token = lexer.token;
791             long hash;
792             if (token != Token.LEFT
793                     && token != Token.RIGHT
794                     && token != Token.FULL
795                     && token != Token.OUTER
796                     && !(token == Token.IDENTIFIER
797                         && ((hash = lexer.hash_lower()) == FnvHash.Constants.STRAIGHT_JOIN
798                             || hash == FnvHash.Constants.CROSS)))
799             {
800                 string _alias = tableAlias();
801                 if (_alias !is null) {
802                     tableSource.setAlias(_alias);
803 
804                     if (lexer.token == Token.WHERE) {
805                         return tableSource;
806                     }
807 
808                     return parseTableSourceRest(tableSource);
809                 }
810             }
811         }
812 
813         SQLJoinTableSource.JoinType joinType = null;
814 
815         bool natural = lexer.identifierEquals(FnvHash.Constants.NATURAL) && DBType.MYSQL.opEquals(dbType);
816         if (natural) {
817             lexer.nextToken();
818         }
819 
820         if (lexer.token == Token.LEFT) {
821             lexer.nextToken();
822 
823             if (lexer.identifierEquals(FnvHash.Constants.SEMI)) {
824                 lexer.nextToken();
825                 joinType = SQLJoinTableSource.JoinType.LEFT_SEMI_JOIN;
826             } else if (lexer.identifierEquals(FnvHash.Constants.ANTI)) {
827                 lexer.nextToken();
828                 joinType = SQLJoinTableSource.JoinType.LEFT_ANTI_JOIN;
829             } else if (lexer.token == Token.OUTER) {
830                 lexer.nextToken();
831                 joinType = SQLJoinTableSource.JoinType.LEFT_OUTER_JOIN;
832             } else {
833                 joinType = SQLJoinTableSource.JoinType.LEFT_OUTER_JOIN;
834             }
835 
836             accept(Token.JOIN);
837 
838         } else if (lexer.token == Token.RIGHT) {
839             lexer.nextToken();
840             if (lexer.token == Token.OUTER) {
841                 lexer.nextToken();
842             }
843             accept(Token.JOIN);
844             joinType = SQLJoinTableSource.JoinType.RIGHT_OUTER_JOIN;
845         } else if (lexer.token == Token.FULL) {
846             lexer.nextToken();
847             if (lexer.token == Token.OUTER) {
848                 lexer.nextToken();
849             }
850             accept(Token.JOIN);
851             joinType = SQLJoinTableSource.JoinType.FULL_OUTER_JOIN;
852         } else if (lexer.token == Token.INNER) {
853             lexer.nextToken();
854             accept(Token.JOIN);
855             joinType = SQLJoinTableSource.JoinType.INNER_JOIN;
856         } else if (lexer.token == Token.JOIN) {
857             lexer.nextToken();
858             joinType = SQLJoinTableSource.JoinType.JOIN;
859         } else if (lexer.token == Token.COMMA) {
860             lexer.nextToken();
861             joinType = SQLJoinTableSource.JoinType.COMMA;
862         } else if (lexer.identifierEquals(FnvHash.Constants.STRAIGHT_JOIN)) {
863             lexer.nextToken();
864             joinType = SQLJoinTableSource.JoinType.STRAIGHT_JOIN;
865         } else if (lexer.identifierEquals(FnvHash.Constants.CROSS)) {
866             lexer.nextToken();
867             if (lexer.token == Token.JOIN) {
868                 lexer.nextToken();
869                 joinType = SQLJoinTableSource.JoinType.CROSS_JOIN;
870             } else if (lexer.identifierEquals(FnvHash.Constants.APPLY)) {
871                 lexer.nextToken();
872                 joinType = SQLJoinTableSource.JoinType.CROSS_APPLY;
873             }
874         } else if (lexer.token == Token.OUTER) {
875             lexer.nextToken();
876             if (lexer.identifierEquals(FnvHash.Constants.APPLY)) {
877                 lexer.nextToken();
878                 joinType = SQLJoinTableSource.JoinType.OUTER_APPLY;
879             }
880         }
881 
882         if (joinType.name.length != 0) {
883             SQLJoinTableSource join = new SQLJoinTableSource();
884             join.setLeft(tableSource);
885             join.setJoinType(joinType);
886 
887 
888             SQLTableSource rightTableSource;
889             if (lexer.token == Token.LPAREN) {
890                 lexer.nextToken();
891                 if (lexer.token == Token.SELECT) {
892                     SQLSelect select = this.select();
893                     rightTableSource = new SQLSubqueryTableSource(select);
894                 } else  {
895                     rightTableSource = this.parseTableSource();
896                 }
897                 accept(Token.RPAREN);
898             } else {
899                 SQLExpr expr = this.expr();
900                 rightTableSource = new SQLExprTableSource(expr);
901                 primaryTableSourceRest(rightTableSource);
902             }
903 
904             if (lexer.token == Token.USING
905                 ||lexer.identifierEquals(FnvHash.Constants.USING))
906             {
907                 Lexer.SavePoint savePoint = lexer.mark();
908                 lexer.nextToken();
909 
910                 if (lexer.token == Token.LPAREN) {
911                     lexer.nextToken();
912                     join.setRight(rightTableSource);
913                     this.exprParser.exprList(join.getUsing(), join);
914                     accept(Token.RPAREN);
915                 } else if (lexer.token == Token.IDENTIFIER) {
916                     lexer.reset(savePoint);
917                     join.setRight(rightTableSource);
918                     return join;
919                 } else {
920                     join.setAlias(this.tableAlias());
921                 }
922             } else {
923                 rightTableSource.setAlias(this.tableAlias());
924 
925                 primaryTableSourceRest(rightTableSource);
926             }
927 
928             if (lexer.token == Token.WITH) {
929                 lexer.nextToken();
930                 accept(Token.LPAREN);
931 
932                 for (;;) {
933                     SQLExpr hintExpr = this.expr();
934                     SQLExprHint hint = new SQLExprHint(hintExpr);
935                     hint.setParent(tableSource);
936                     rightTableSource.getHints().add(hint);
937                     if (lexer.token == Token.COMMA) {
938                         lexer.nextToken();
939                         continue;
940                     } else {
941                         break;
942                     }
943                 }
944 
945                 accept(Token.RPAREN);
946             }
947 
948             join.setRight(rightTableSource);
949 
950             if (!natural) {
951                 if (tableSource.aliasHashCode64() == FnvHash.Constants.NATURAL && DBType.MYSQL.opEquals(dbType)) {
952                     tableSource.setAlias(null);
953                     natural = true;
954                 }
955             }
956             join.setNatural(natural);
957 
958             if (lexer.token == Token.ON) {
959                 lexer.nextToken();
960                 join.setCondition(expr());
961             } else if (lexer.token == Token.USING
962                     || lexer.identifierEquals(FnvHash.Constants.USING)) {
963                 Lexer.SavePoint savePoint = lexer.mark();
964                 lexer.nextToken();
965                 if (lexer.token == Token.LPAREN) {
966                     lexer.nextToken();
967                     this.exprParser.exprList(join.getUsing(), join);
968                     accept(Token.RPAREN);
969                 } else {
970                     lexer.reset(savePoint);
971                 }
972             }
973 
974             return parseTableSourceRest(join);
975         }
976 
977         if (tableSource.aliasHashCode64() == FnvHash.Constants.LATERAL
978                 && lexer.token() == Token.VIEW) {
979             return parseLateralView(tableSource);
980         }
981 
982         if (lexer.identifierEquals(FnvHash.Constants.LATERAL)) {
983             lexer.nextToken();
984             return parseLateralView(tableSource);
985         }
986 
987         return tableSource;
988     }
989 
990     public SQLExpr expr() {
991         return this.exprParser.expr();
992     }
993 
994     public SQLOrderBy parseOrderBy() {
995         return this.exprParser.parseOrderBy();
996     }
997 
998     public void acceptKeyword(string ident) {
999         if (lexer.token == Token.IDENTIFIER && equalsIgnoreCase(ident, lexer.stringVal())) {
1000             lexer.nextToken();
1001         } else {
1002             setErrorEndPos(lexer.pos());
1003             throw new ParserException("syntax error, expect " ~ ident ~ ", actual " ~ lexer.token ~ ", " ~ lexer.info());
1004         }
1005     }
1006 
1007     public void parseFetchClause(SQLSelectQueryBlock queryBlock) {
1008         if (lexer.token == Token.LIMIT) {
1009             SQLLimit limit = this.exprParser.parseLimit();
1010             queryBlock.setLimit(limit);
1011             return;
1012         }
1013 
1014         if (lexer.identifierEquals(FnvHash.Constants.OFFSET) || lexer.token == Token.OFFSET) {
1015             lexer.nextToken();
1016             SQLExpr offset = this.exprParser.primary();
1017             queryBlock.setOffset(offset);
1018             if (lexer.identifierEquals(FnvHash.Constants.ROW) || lexer.identifierEquals(FnvHash.Constants.ROWS)) {
1019                 lexer.nextToken();
1020             }
1021         }
1022 
1023         if (lexer.token == Token.FETCH) {
1024             lexer.nextToken();
1025             if (lexer.token == Token.FIRST
1026                     || lexer.token == Token.NEXT
1027                     || lexer.identifierEquals(FnvHash.Constants.NEXT)) {
1028                 lexer.nextToken();
1029             } else {
1030                 acceptIdentifier("FIRST");
1031             }
1032             SQLExpr first = this.exprParser.primary();
1033             queryBlock.setFirst(first);
1034             if (lexer.identifierEquals(FnvHash.Constants.ROW) || lexer.identifierEquals(FnvHash.Constants.ROWS)) {
1035                 lexer.nextToken();
1036             }
1037 
1038             if (lexer.token == Token.ONLY) {
1039                 lexer.nextToken();
1040             } else {
1041                 acceptIdentifier("ONLY");
1042             }
1043         }
1044     }
1045 
1046     protected void parseHierachical(SQLSelectQueryBlock queryBlock) {
1047         if (lexer.token == Token.CONNECT || lexer.identifierEquals(FnvHash.Constants.CONNECT)) {
1048             lexer.nextToken();
1049             accept(Token.BY);
1050 
1051             if (lexer.token == Token.PRIOR || lexer.identifierEquals(FnvHash.Constants.PRIOR)) {
1052                 lexer.nextToken();
1053                 queryBlock.setPrior(true);
1054             }
1055 
1056             if (lexer.identifierEquals(FnvHash.Constants.NOCYCLE)) {
1057                 queryBlock.setNoCycle(true);
1058                 lexer.nextToken();
1059 
1060                 if (lexer.token == Token.PRIOR) {
1061                     lexer.nextToken();
1062                     queryBlock.setPrior(true);
1063                 }
1064             }
1065             queryBlock.setConnectBy(this.exprParser.expr());
1066         }
1067 
1068         if (lexer.token == Token.START || lexer.identifierEquals(FnvHash.Constants.START)) {
1069             lexer.nextToken();
1070             accept(Token.WITH);
1071 
1072             queryBlock.setStartWith(this.exprParser.expr());
1073         }
1074 
1075         if (lexer.token == Token.CONNECT || lexer.identifierEquals(FnvHash.Constants.CONNECT)) {
1076             lexer.nextToken();
1077             accept(Token.BY);
1078 
1079             if (lexer.token == Token.PRIOR || lexer.identifierEquals(FnvHash.Constants.PRIOR)) {
1080                 lexer.nextToken();
1081                 queryBlock.setPrior(true);
1082             }
1083 
1084             if (lexer.identifierEquals(FnvHash.Constants.NOCYCLE)) {
1085                 queryBlock.setNoCycle(true);
1086                 lexer.nextToken();
1087 
1088                 if (lexer.token == Token.PRIOR || lexer.identifierEquals(FnvHash.Constants.PRIOR)) {
1089                     lexer.nextToken();
1090                     queryBlock.setPrior(true);
1091                 }
1092             }
1093             queryBlock.setConnectBy(this.exprParser.expr());
1094         }
1095     }
1096 
1097     protected SQLTableSource parseLateralView(SQLTableSource tableSource) {
1098         accept(Token.VIEW);
1099         if ("LATERAL".equalsIgnoreCase(tableSource.getAlias())) {
1100             tableSource.setAlias(null);
1101         }
1102         SQLLateralViewTableSource lateralViewTabSrc = new SQLLateralViewTableSource();
1103         lateralViewTabSrc.setTableSource(tableSource);
1104 
1105         SQLMethodInvokeExpr udtf = cast(SQLMethodInvokeExpr) this.exprParser.expr();
1106         lateralViewTabSrc.setMethod(udtf);
1107 
1108         string _alias = as();
1109         lateralViewTabSrc.setAlias(_alias);
1110 
1111         accept(Token.AS);
1112 
1113         this.exprParser.names(lateralViewTabSrc.getColumns());
1114 
1115         return parseTableSourceRest(lateralViewTabSrc);
1116     }
1117 }