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.ast.statement.SQLSelect;
17 
18 
19 import hunt.collection;
20 
21 import hunt.sql.SQLUtils;
22 import hunt.sql.ast;
23 import hunt.sql.ast.expr.SQLAllColumnExpr;
24 //import hunt.sql.dialect.oracle.ast.OracleSQLObject; //@gxc
25 import hunt.sql.visitor.SQLASTOutputVisitor;
26 import hunt.sql.visitor.SQLASTVisitor;
27 import hunt.sql.util.DBType;
28 import hunt.sql.ast.statement.SQLWithSubqueryClause;
29 import hunt.sql.ast.statement.SQLSelectQuery;
30 import hunt.sql.ast.statement.SQLSelectQueryBlock;
31 import hunt.sql.ast.statement.SQLUnionQuery;
32 import hunt.util.StringBuilder;
33 
34 public class SQLSelect : SQLObjectImpl {
35 
36     protected SQLWithSubqueryClause withSubQuery;
37     protected SQLSelectQuery        query;
38     protected SQLOrderBy            orderBy;
39 
40     protected List!SQLHint         hints;
41 
42     protected SQLObject             restriction;
43 
44     protected bool               forBrowse;
45     protected List!string          forXmlOptions = null;
46     protected SQLExpr               xmlPath;
47 
48     protected SQLExpr                rowCount;
49     protected SQLExpr                offset;
50 
51     public this(){
52 
53     }
54 
55     public List!SQLHint getHints() {
56         if (hints is null) {
57             hints = new ArrayList!SQLHint(2);
58         }
59         return hints;
60     }
61     
62     public int getHintsSize() {
63         if (hints is null) {
64             return 0;
65         }
66         return hints.size();
67     }
68 
69     public this(SQLSelectQuery query){
70         this.setQuery(query);
71     }
72 
73     public SQLWithSubqueryClause getWithSubQuery() {
74         return withSubQuery;
75     }
76 
77     public void setWithSubQuery(SQLWithSubqueryClause withSubQuery) {
78         this.withSubQuery = withSubQuery;
79     }
80 
81     public SQLSelectQuery getQuery() {
82         return this.query;
83     }
84 
85     public void setQuery(SQLSelectQuery query) {
86         if (query !is null) {
87             query.setParent(this);
88         }
89         this.query = query;
90     }
91 
92     public SQLSelectQueryBlock getQueryBlock() {
93         if (cast(SQLSelectQueryBlock)(query) !is null ) {
94             return cast(SQLSelectQueryBlock) query;
95         }
96 
97         return null;
98     }
99 
100     public SQLOrderBy getOrderBy() {
101         return this.orderBy;
102     }
103 
104     public void setOrderBy(SQLOrderBy orderBy) {
105         if (orderBy !is null) {
106             orderBy.setParent(this);
107         }
108         this.orderBy = orderBy;
109     }
110 
111     override  protected void accept0(SQLASTVisitor visitor) {
112         if (visitor.visit(this)) {
113             acceptChild(visitor, this.withSubQuery);
114             acceptChild(visitor, this.query);
115             acceptChild(visitor, this.restriction);
116             acceptChild(visitor, this.orderBy);
117             acceptChild!SQLHint(visitor, this.hints);
118             acceptChild(visitor, this.offset);
119             acceptChild(visitor, this.rowCount);
120         }
121 
122         visitor.endVisit(this);
123     }
124 
125     override
126     public size_t toHash() @trusted nothrow {
127          int prime = 31;
128         size_t result = 1;
129         result = prime * result + ((orderBy is null) ? 0 : (cast(Object)orderBy).toHash());
130         result = prime * result + ((query is null) ? 0 : (cast(Object)query).toHash());
131         result = prime * result + ((withSubQuery is null) ? 0 : (cast(Object)withSubQuery).toHash());
132         return result;
133     }
134 
135     override
136     public bool opEquals(Object obj) {
137         if (this == obj) return true;
138         if (obj is null) return false;
139         if (typeid(this) != typeid(obj)) return false;
140         SQLSelect other = cast(SQLSelect) obj;
141         if (orderBy is null) {
142             if (other.orderBy !is null) return false;
143         } else if (!(cast(Object)(orderBy)).opEquals(cast(Object)(other.orderBy))) return false;
144         if (query is null) {
145             if (other.query !is null) return false;
146         } else if (!(cast(Object)(query)).opEquals(cast(Object)(other.query))) return false;
147         if (withSubQuery is null) {
148             if (other.withSubQuery !is null) return false;
149         } else if (!(cast(Object)(withSubQuery)).opEquals(cast(Object)(other.withSubQuery))) return false;
150         return true;
151     }
152 
153     override public void output(StringBuilder buf) {
154         string dbType = null;
155 
156         SQLObject parent = this.getParent();
157         if (cast(SQLStatement)(parent) !is null ) {
158             dbType = (cast(SQLStatement) parent).getDbType();
159         }
160 
161         // if (dbType is null && (cast(OracleSQLObject)parent) !is null ) {
162         //     dbType = DBType.ORACLE;
163         // }    //@gxc
164 
165         if (dbType is null && (cast(SQLSelectQueryBlock)query) !is null ) {
166             dbType = (cast(SQLSelectQueryBlock) query).dbType;
167         }
168 
169         SQLASTOutputVisitor visitor = SQLUtils.createOutputVisitor(buf, dbType);
170         this.accept(visitor);
171     }
172 
173     override public string toString() {
174         SQLObject parent = this.getParent();
175         if (cast(SQLStatement)(parent) !is null ) {
176             string dbType = (cast(SQLStatement) parent).getDbType();
177             
178             if (dbType !is null) {
179                 return SQLUtils.toSQLString(this, dbType);
180             }
181         }
182 
183         // if (cast(OracleSQLObject)(parent) !is null ) {
184         //     return SQLUtils.toSQLString(this, DBType.ORACLE);
185         // }    //@gxc
186 
187         if (cast(SQLSelectQueryBlock)(query) !is null ) {
188             string dbType = (cast(SQLSelectQueryBlock) query).dbType;
189 
190             if (dbType !is null) {
191                 return SQLUtils.toSQLString(this, dbType);
192             }
193         }
194         
195         return super.toString();
196     }
197 
198     override public SQLSelect clone() {
199         SQLSelect x = new SQLSelect();
200 
201         x.withSubQuery = this.withSubQuery;
202         if (query !is null) {
203             x.setQuery(query.clone());
204         }
205 
206         if (orderBy !is null) {
207             x.setOrderBy(this.orderBy.clone());
208         }
209         if (restriction !is null) {
210             x.setRestriction(restriction.clone());
211         }
212 
213         if (this.hints !is null) {
214             foreach (SQLHint hint ; this.hints) {
215                 x.hints.add(hint);
216             }
217         }
218 
219         x.forBrowse = forBrowse;
220 
221         if (forXmlOptions !is null) {
222             x.forXmlOptions = (forXmlOptions);
223         }
224 
225         if (xmlPath !is null) {
226             x.setXmlPath(xmlPath.clone());
227         }
228 
229         if (rowCount !is null) {
230             x.setRowCount(rowCount.clone());
231         }
232 
233         if (offset !is null) {
234             x.setOffset(offset.clone());
235         }
236 
237         return x;
238     }
239 
240     public bool isSimple() {
241         return withSubQuery is null
242                 && (hints is null || hints.size() == 0)
243                 && restriction is null
244                 && (!forBrowse)
245                 && (forXmlOptions is null || forXmlOptions.size() == 0)
246                 && xmlPath is null
247                 && rowCount is null
248                 && offset is null;
249     }
250 
251     public SQLObject getRestriction() {
252         return this.restriction;
253     }
254 
255     public void setRestriction(SQLObject restriction) {
256         if (restriction !is null) {
257             restriction.setParent(this);
258         }
259         this.restriction = restriction;
260     }
261 
262     public bool isForBrowse() {
263         return forBrowse;
264     }
265 
266     public void setForBrowse(bool forBrowse) {
267         this.forBrowse = forBrowse;
268     }
269 
270     public List!string getForXmlOptions() {
271         if (forXmlOptions is null) {
272             forXmlOptions = new ArrayList!string(4);
273         }
274 
275         return forXmlOptions;
276     }
277 
278     public int getForXmlOptionsSize() {
279         if (forXmlOptions is null) {
280             return 0;
281         }
282         return forXmlOptions.size();
283     }
284 
285     public SQLExpr getRowCount() {
286         return rowCount;
287     }
288 
289     public void setRowCount(SQLExpr rowCount) {
290         if (rowCount !is null) {
291             rowCount.setParent(this);
292         }
293 
294         this.rowCount = rowCount;
295     }
296 
297     public SQLExpr getOffset() {
298         return offset;
299     }
300 
301     public void setOffset(SQLExpr offset) {
302         if (offset !is null) {
303             offset.setParent(this);
304         }
305         this.offset = offset;
306     }
307 
308     public SQLExpr getXmlPath() {
309         return xmlPath;
310     }
311 
312     public void setXmlPath(SQLExpr xmlPath) {
313         if (xmlPath !is null) {
314             xmlPath.setParent(this);
315         }
316         this.xmlPath = xmlPath;
317     }
318 
319     public SQLSelectQueryBlock getFirstQueryBlock() {
320         if (cast(SQLSelectQueryBlock)(query) !is null ) {
321             return cast(SQLSelectQueryBlock) query;
322         }
323 
324         if (cast(SQLUnionQuery)(query) !is null ) {
325             return (cast(SQLUnionQuery) query).getFirstQueryBlock();
326         }
327 
328         return null;
329     }
330 
331     public bool addWhere(SQLExpr where) {
332         if (where is null) {
333             return false;
334         }
335 
336         if (cast(SQLSelectQueryBlock)(query) !is null ) {
337             (cast(SQLSelectQueryBlock) query).addWhere(where);
338             return true;
339         }
340 
341         if (cast(SQLUnionQuery)(query) !is null ) {
342             SQLSelectQueryBlock queryBlock = new SQLSelectQueryBlock();
343             queryBlock.setFrom(new SQLSelect(query), "u");
344             queryBlock.addSelectItem(new SQLAllColumnExpr());
345             queryBlock.setParent(queryBlock);
346             query = queryBlock;
347             return true;
348         }
349 
350         return false;
351     }
352 }