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.postgresql.parser.PGSelectParser;
17 
18 import hunt.collection;
19 
20 import hunt.sql.ast.SQLExpr;
21 import hunt.sql.ast.SQLLimit;
22 import hunt.sql.ast.SQLParameter;
23 import hunt.sql.ast.SQLSetQuantifier;
24 import hunt.sql.ast.expr.SQLIdentifierExpr;
25 import hunt.sql.ast.statement.SQLExprTableSource;
26 import hunt.sql.ast.statement.SQLSelectQuery;
27 import hunt.sql.ast.statement.SQLSelectQueryBlock;
28 import hunt.sql.ast.statement.SQLTableSource;
29 import hunt.sql.dialect.postgresql.ast.stmt.PGFunctionTableSource;
30 import hunt.sql.dialect.postgresql.ast.stmt.PGSelectQueryBlock;
31 // import hunt.sql.dialect.postgresql.ast.stmt.PGSelectQueryBlock.IntoOption;
32 import hunt.sql.dialect.postgresql.ast.stmt.PGValuesQuery;
33 import hunt.sql.parser;
34 import hunt.sql.ast.statement.SQLTableSource;
35 import hunt.sql.dialect.postgresql.parser.PGExprParser;
36 
37 
38 public class PGSelectParser : SQLSelectParser {
39 
40     public this(SQLExprParser exprParser){
41         super(exprParser);
42     }
43 
44     public this(SQLExprParser exprParser, SQLSelectListCache selectListCache){
45         super(exprParser, selectListCache);
46     }
47 
48     public this(string sql){
49         this(new PGExprParser(sql));
50     }
51 
52     protected SQLExprParser createExprParser() {
53         return new PGExprParser(lexer);
54     }
55 
56     override
57     public SQLSelectQuery query() {
58         if (lexer.token() == Token.VALUES) {
59             lexer.nextToken();
60             accept(Token.LPAREN);
61             PGValuesQuery valuesQuery = new PGValuesQuery();
62             this.exprParser.exprList(valuesQuery.getValues(), valuesQuery);
63             accept(Token.RPAREN);
64             return queryRest(valuesQuery);
65         }
66 
67         if (lexer.token() == Token.LPAREN) {
68             lexer.nextToken();
69 
70             SQLSelectQuery select = query();
71             if (cast(SQLSelectQueryBlock)(select) !is null) {
72                 (cast(SQLSelectQueryBlock) select).setParenthesized(true);
73             }
74             accept(Token.RPAREN);
75 
76             return queryRest(select);
77         }
78 
79         PGSelectQueryBlock queryBlock = new PGSelectQueryBlock();
80 
81         if (lexer.token() == Token.SELECT) {
82             lexer.nextToken();
83 
84             if (lexer.token() == Token.COMMENT) {
85                 lexer.nextToken();
86             }
87 
88             if (lexer.token() == Token.DISTINCT) {
89                 queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT);
90                 lexer.nextToken();
91 
92                 if (lexer.token() == Token.ON) {
93                     lexer.nextToken();
94 
95                     for (;;) {
96                         SQLExpr expr = this.createExprParser().expr();
97                         queryBlock.getDistinctOn().add(expr);
98                         if (lexer.token() == Token.COMMA) {
99                             lexer.nextToken();
100                             continue;
101                         } else {
102                             break;
103                         }
104                     }
105                 }
106             } else if (lexer.token() == Token.ALL) {
107                 queryBlock.setDistionOption(SQLSetQuantifier.ALL);
108                 lexer.nextToken();
109             }
110 
111             parseSelectList(queryBlock);
112 
113             if (lexer.token() == Token.INTO) {
114                 lexer.nextToken();
115 
116                 if (lexer.token() == Token.TEMPORARY) {
117                     lexer.nextToken();
118                     queryBlock.setIntoOption(PGSelectQueryBlock.IntoOption.TEMPORARY);
119                 } else if (lexer.token() == Token.TEMP) {
120                     lexer.nextToken();
121                     queryBlock.setIntoOption(PGSelectQueryBlock.IntoOption.TEMP);
122                 } else if (lexer.token() == Token.UNLOGGED) {
123                     lexer.nextToken();
124                     queryBlock.setIntoOption(PGSelectQueryBlock.IntoOption.UNLOGGED);
125                 }
126 
127                 if (lexer.token() == Token.TABLE) {
128                     lexer.nextToken();
129                 }
130 
131                 SQLExpr name = this.createExprParser().name();
132 
133                 queryBlock.setInto(new SQLExprTableSource(name));
134             }
135         }
136 
137         parseFrom(queryBlock);
138 
139         parseWhere(queryBlock);
140 
141         parseGroupBy(queryBlock);
142 
143         if (lexer.token() == Token.WINDOW) {
144             this.parseWindow(queryBlock);
145         }
146 
147         queryBlock.setOrderBy(this.createExprParser().parseOrderBy());
148 
149         for (;;) {
150             if (lexer.token() == Token.LIMIT) {
151                 SQLLimit limit = new SQLLimit();
152 
153                 lexer.nextToken();
154                 if (lexer.token() == Token.ALL) {
155                     limit.setRowCount(new SQLIdentifierExpr("ALL"));
156                     lexer.nextToken();
157                 } else {
158                     limit.setRowCount(expr());
159                 }
160 
161                 queryBlock.setLimit(limit);
162             } else if (lexer.token() == Token.OFFSET) {
163                 SQLLimit limit = queryBlock.getLimit();
164                 if (limit is null) {
165                     limit = new SQLLimit();
166                     queryBlock.setLimit(limit);
167                 }
168                 lexer.nextToken();
169                 SQLExpr offset = expr();
170                 limit.setOffset(offset);
171 
172                 if (lexer.token() == Token.ROW || lexer.token() == Token.ROWS) {
173                     lexer.nextToken();
174                 }
175             } else {
176                 break;
177             }
178         }
179 
180         if (lexer.token() == Token.FETCH) {
181             lexer.nextToken();
182             PGSelectQueryBlock.FetchClause fetch = new PGSelectQueryBlock.FetchClause();
183 
184             if (lexer.token() == Token.FIRST) {
185                 fetch.setOption(PGSelectQueryBlock.FetchClause.Option.FIRST);
186             } else if (lexer.token() == Token.NEXT) {
187                 fetch.setOption(PGSelectQueryBlock.FetchClause.Option.NEXT);
188             } else {
189                 throw new ParserException("expect 'FIRST' or 'NEXT'. " ~ lexer.info());
190             }
191 
192             SQLExpr count = expr();
193             fetch.setCount(count);
194 
195             if (lexer.token() == Token.ROW || lexer.token() == Token.ROWS) {
196                 lexer.nextToken();
197             } else {
198                 throw new ParserException("expect 'ROW' or 'ROWS'. " ~ lexer.info());
199             }
200 
201             if (lexer.token() == Token.ONLY) {
202                 lexer.nextToken();
203             } else {
204                 throw new ParserException("expect 'ONLY'. " ~ lexer.info());
205             }
206 
207             queryBlock.setFetch(fetch);
208         }
209 
210         if (lexer.token() == Token.FOR) {
211             lexer.nextToken();
212 
213             PGSelectQueryBlock.ForClause forClause = new PGSelectQueryBlock.ForClause();
214 
215             if (lexer.token() == Token.UPDATE) {
216                 forClause.setOption(PGSelectQueryBlock.ForClause.Option.UPDATE);
217                 lexer.nextToken();
218             } else if (lexer.token() == Token.SHARE) {
219                 forClause.setOption(PGSelectQueryBlock.ForClause.Option.SHARE);
220                 lexer.nextToken();
221             } else {
222                 throw new ParserException("expect 'FIRST' or 'NEXT'. " ~ lexer.info());
223             }
224 
225             if (lexer.token() == Token.OF) {
226                 for (;;) {
227                     SQLExpr expr = this.createExprParser().expr();
228                     forClause.getOf().add(expr);
229                     if (lexer.token() == Token.COMMA) {
230                         lexer.nextToken();
231                         continue;
232                     } else {
233                         break;
234                     }
235                 }
236             }
237 
238             if (lexer.token() == Token.NOWAIT) {
239                 lexer.nextToken();
240                 forClause.setNoWait(true);
241             }
242 
243             queryBlock.setForClause(forClause);
244         }
245 
246         return queryRest(queryBlock);
247     }
248 
249     override protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) {
250         if (lexer.token() == Token.AS && cast(SQLExprTableSource)(tableSource) !is null) {
251             lexer.nextToken();
252 
253             string _alias = null;
254             if (lexer.token() == Token.IDENTIFIER) {
255                 _alias = lexer.stringVal();
256                 lexer.nextToken();
257             }
258 
259             if (lexer.token() == Token.LPAREN) {
260                 SQLExprTableSource exprTableSource = cast(SQLExprTableSource) tableSource;
261 
262                 PGFunctionTableSource functionTableSource = new PGFunctionTableSource(exprTableSource.getExpr());
263                 if (_alias !is null) {
264                     functionTableSource.setAlias(_alias);
265                 }
266                 
267                 lexer.nextToken();
268                 parserParameters(functionTableSource.getParameters());
269                 accept(Token.RPAREN);
270 
271                 return super.parseTableSourceRest(functionTableSource);
272             }
273             if (_alias !is null) {
274                 tableSource.setAlias(_alias);
275                 return super.parseTableSourceRest(tableSource);
276             }
277         }
278 
279         return super.parseTableSourceRest(tableSource);
280     }
281 
282     private void parserParameters(List!(SQLParameter) parameters) {
283         for (;;) {
284             SQLParameter parameter = new SQLParameter();
285 
286             parameter.setName(this.exprParser.name());
287             parameter.setDataType(this.exprParser.parseDataType());
288 
289             parameters.add(parameter);
290             if (lexer.token() == Token.COMMA || lexer.token() == Token.SEMI) {
291                 lexer.nextToken();
292             }
293 
294             if (lexer.token() != Token.BEGIN && lexer.token() != Token.RPAREN) {
295                 continue;
296             }
297 
298             break;
299         }
300     }
301 }