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.builder.impl.SQLSelectBuilderImpl;
17 
18 import hunt.collection;
19 
20 import hunt.sql.SQLUtils;
21 import hunt.sql.ast.SQLExpr;
22 import hunt.sql.ast.SQLOrderBy;
23 import hunt.sql.ast.SQLStatement;
24 import hunt.sql.ast.expr.SQLBinaryOperator;
25 import hunt.sql.ast.expr.SQLIdentifierExpr;
26 import hunt.sql.ast.expr.SQLIntegerExpr;
27 import hunt.sql.ast.statement.SQLExprTableSource;
28 import hunt.sql.ast.statement.SQLJoinTableSource;
29 import hunt.sql.ast.statement.SQLSelect;
30 import hunt.sql.ast.statement.SQLSelectGroupByClause;
31 import hunt.sql.ast.statement.SQLSelectItem;
32 import hunt.sql.ast.statement.SQLSelectOrderByItem;
33 import hunt.sql.ast.statement.SQLSelectQuery;
34 import hunt.sql.ast.statement.SQLSelectQueryBlock;
35 import hunt.sql.ast.statement.SQLSelectStatement;
36 import hunt.sql.builder.SQLSelectBuilder;
37 import hunt.sql.ast.SQLSetQuantifier;
38 // import hunt.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock;
39 import hunt.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
40 import hunt.sql.ast.SQLLimit;
41 // import hunt.sql.dialect.odps.ast.OdpsSelectQueryBlock;
42 // import hunt.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;
43 import hunt.sql.dialect.postgresql.ast.stmt.PGSelectQueryBlock;
44 // import hunt.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;
45 // import hunt.sql.dialect.sqlserver.ast.SQLServerTop;
46 import hunt.sql.util.DBType;
47 import hunt.sql.builder.SQLBuilder;
48 
49 class SQLSelectBuilderImpl : SQLSelectBuilder {
50 
51     private SQLSelectStatement stmt;
52     private string             dbType;
53 
54     this(string dbType){
55         this(new SQLSelectStatement(), dbType);
56     }
57     
58     this(string sql, string dbType){
59         List!SQLStatement stmtList = SQLUtils.parseStatements(sql, dbType);
60 
61         if (stmtList.size() == 0) {
62             throw new Exception("not support empty-statement :" ~ sql);
63         }
64 
65         if (stmtList.size() > 1) {
66             throw new Exception("not support multi-statement :" ~ sql);
67         }
68 
69         SQLSelectStatement stmt = cast(SQLSelectStatement) stmtList.get(0);
70         this.stmt = stmt;
71         this.dbType = dbType;
72     }
73 
74     this(SQLSelectStatement stmt, string dbType){
75         this.stmt = stmt;
76         this.dbType = dbType;
77     }
78 
79     SQLSelect getSQLSelect() {
80         if (stmt.getSelect() is null) {
81             stmt.setSelect(createSelect());
82         }
83         return stmt.getSelect();
84     }
85 
86     SQLSelectStatement getSQLSelectStatement() {
87         return stmt;
88     }
89 
90     override
91     SQLBuilder select(string[] columns...) {
92         SQLSelectQueryBlock queryBlock = getQueryBlock();
93 
94         foreach (string column ; columns) {
95             SQLSelectItem selectItem = SQLUtils.toSelectItem(column, dbType);
96             queryBlock.addSelectItem(selectItem);
97         }
98 
99 
100         return this;
101     }
102 
103     override
104     SQLBuilder selectWithAlias(string column, string _alias) {
105         SQLSelectQueryBlock queryBlock = getQueryBlock();
106 
107         SQLExpr columnExpr = SQLUtils.toSQLExpr(column, dbType);
108         SQLSelectItem selectItem = new SQLSelectItem(columnExpr, _alias);
109         queryBlock.addSelectItem(selectItem);
110 
111         return this;
112     }
113 
114     override
115     SQLBuilder from(string table) {
116         return from(table, null);
117     }
118 
119     override
120     SQLBuilder from(string table, string _alias) {
121         SQLSelectQueryBlock queryBlock = getQueryBlock();
122         SQLExprTableSource from = new SQLExprTableSource(new SQLIdentifierExpr(table), _alias);
123         queryBlock.setFrom(from);
124 
125         return this;
126     }
127 
128     override
129     SQLBuilder orderBy(string[] columns...) {
130         // SQLSelect select = this.getSQLSelect();
131         SQLSelectQueryBlock queryBlock = getQueryBlock();
132 
133         SQLOrderBy orderBy = queryBlock.getOrderBy();
134         if (orderBy is null) {
135             orderBy = createOrderBy();
136             queryBlock.setOrderBy(orderBy);
137         }
138 
139         foreach (string column ; columns) {
140             SQLSelectOrderByItem orderByItem = SQLUtils.toOrderByItem(column, dbType);
141             orderBy.addItem(orderByItem);
142         }
143 
144         return this;
145     }
146 
147     override
148     SQLBuilder groupBy(string expr) {
149         SQLSelectQueryBlock queryBlock = getQueryBlock();
150 
151         SQLSelectGroupByClause groupBy = queryBlock.getGroupBy();
152         if (groupBy is null) {
153             groupBy = createGroupBy();
154             queryBlock.setGroupBy(groupBy);
155         }
156 
157         SQLExpr exprObj = SQLUtils.toSQLExpr(expr, dbType);
158         groupBy.addItem(exprObj);
159 
160         return this;
161     }
162 
163     override
164     SQLBuilder having(string expr) {
165         SQLSelectQueryBlock queryBlock = getQueryBlock();
166 
167         SQLSelectGroupByClause groupBy = queryBlock.getGroupBy();
168         if (groupBy is null) {
169             groupBy = createGroupBy();
170             queryBlock.setGroupBy(groupBy);
171         }
172 
173         SQLExpr exprObj = SQLUtils.toSQLExpr(expr, dbType);
174         groupBy.setHaving(exprObj);
175 
176         return this;
177     }
178 
179     override
180     SQLBuilder into(string expr) {
181         SQLSelectQueryBlock queryBlock = getQueryBlock();
182 
183         SQLExpr exprObj = SQLUtils.toSQLExpr(expr, dbType);
184         queryBlock.setInto(exprObj);
185 
186         return this;
187     }
188 
189     override
190     SQLBuilder where(string expr) {
191         SQLSelectQueryBlock queryBlock = getQueryBlock();
192 
193         SQLExpr exprObj = SQLUtils.toSQLExpr(expr, dbType);
194         queryBlock.setWhere(exprObj);
195 
196         return this;
197     }
198 
199     override
200     SQLBuilder whereAnd(string expr) {
201         SQLSelectQueryBlock queryBlock = getQueryBlock();
202         queryBlock.addWhere(SQLUtils.toSQLExpr(expr, dbType));
203 
204         return this;
205     }
206 
207     override
208     SQLBuilder whereOr(string expr) {
209         SQLSelectQueryBlock queryBlock = getQueryBlock();
210 
211         SQLExpr exprObj = SQLUtils.toSQLExpr(expr, dbType);
212         SQLExpr newCondition = SQLUtils.buildCondition(SQLBinaryOperator.BooleanOr, exprObj, false,
213                                                        queryBlock.getWhere());
214         queryBlock.setWhere(newCondition);
215 
216         return this;
217     }
218 
219     override
220     SQLBuilder limit(int rowCount) {
221         auto rowLimit = getQueryBlock().getLimit();
222         if(rowLimit !is null)
223             rowLimit.setRowCount(rowCount);
224         else
225         {
226             getQueryBlock()
227                 .limit(rowCount, 0);
228         }
229         return this;
230     }
231 
232     override
233     SQLBuilder offset(int off) {
234         auto rowLimit = getQueryBlock().getLimit();
235         if(rowLimit !is null)
236             rowLimit.setOffset(off);
237         else
238         {
239             getQueryBlock()
240                 .limit(1, off);
241         }
242         return this;
243     }
244 
245     override
246     SQLBuilder limit(int rowCount, int offset) {
247         getQueryBlock()
248                 .limit(rowCount, offset);
249         return this;
250     }
251 
252     override
253     SQLBuilder join(string table , string _alias = null , string cond = null)
254     {
255         return doJoin(SQLJoinTableSource.JoinType.JOIN,table,_alias,cond);
256     }
257 
258     override
259     SQLBuilder innerJoin(string table , string _alias = null , string cond = null)
260     {
261         return doJoin(SQLJoinTableSource.JoinType.INNER_JOIN,table,_alias,cond);
262     }
263 
264     override
265     SQLBuilder leftJoin(string table , string _alias = null , string cond = null)
266     {
267         return doJoin(SQLJoinTableSource.JoinType.LEFT_OUTER_JOIN,table,_alias,cond);
268     }
269 
270     override
271     SQLBuilder rightJoin(string table , string _alias = null , string cond = null)
272     {
273         return doJoin(SQLJoinTableSource.JoinType.RIGHT_OUTER_JOIN,table,_alias,cond);
274     }
275 
276     private SQLBuilder doJoin(SQLJoinTableSource.JoinType type , string table , string _alias = null , string cond = null)
277     {
278         SQLSelectQueryBlock queryBlock = getQueryBlock();
279         auto  from = queryBlock.getFrom();
280         if(from is null)
281         {
282             throw new Exception("No From Table");
283         }
284         else
285         {
286             auto rightTable = new SQLExprTableSource();
287             rightTable.setExpr(table);
288             rightTable.setAlias(_alias);
289             if(cond is null)
290                 queryBlock.setFrom(new SQLJoinTableSource(from,type,rightTable));
291             else
292                 queryBlock.setFrom(new SQLJoinTableSource(from,type,rightTable,SQLUtils.toSQLExpr(cond)));
293         }
294 
295         return this;
296     }
297 
298 
299     protected SQLSelectQueryBlock getQueryBlock() {
300         SQLSelect select = getSQLSelect();
301         SQLSelectQuery query = select.getQuery();
302         if (query is null) {
303             query = createSelectQueryBlock();
304             select.setQuery(query);
305         }
306 
307         if (!(cast(SQLSelectQueryBlock)(query) !is null)) {
308             throw new Exception("not support from, class : " ~ typeid(query).stringof);
309         }
310 
311         SQLSelectQueryBlock queryBlock = cast(SQLSelectQueryBlock) query;
312         return queryBlock;
313     }
314 
315     protected SQLSelect createSelect() {
316         return new SQLSelect();
317     }
318 
319     protected SQLSelectQuery createSelectQueryBlock() {
320         if (DBType.MYSQL.name == dbType) {
321             return new MySqlSelectQueryBlock();
322         }
323 
324         if (DBType.POSTGRESQL.name == dbType) {
325             return new PGSelectQueryBlock();
326         }
327 
328         // if (DBType.SQL_SERVER.name == dbType) {
329         //     return new SQLServerSelectQueryBlock();
330         // }
331 
332         // if (DBType.ORACLE.name == dbType) {
333         //     return new OracleSelectQueryBlock();
334         // }
335 
336         return new SQLSelectQueryBlock();
337     }
338 
339     protected SQLOrderBy createOrderBy() {
340         return new SQLOrderBy();
341     }
342 
343     protected SQLSelectGroupByClause createGroupBy() {
344         return new SQLSelectGroupByClause();
345     }
346 
347     void setDistinct()
348     {
349         getQueryBlock().setDistionOption(SQLSetQuantifier.DISTINCT);
350         return;
351     }
352 
353     override string toString() {
354         return SQLUtils.toSQLString(stmt, dbType);
355     }
356     
357     string toString(FormatOption option) {
358         return SQLUtils.toSQLString(stmt, dbType, option);
359     }
360 }