1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
4  *
5  * See the LICENSE file for terms of use.
6  */
7 #ifndef WT_DBO_QUERY_IMPL_H_
8 #define WT_DBO_QUERY_IMPL_H_
9 
10 #include <tuple>
11 
12 #include <Wt/Dbo/Exception.h>
13 #include <Wt/Dbo/Field.h>
14 #include <Wt/Dbo/SqlStatement.h>
15 #include <Wt/Dbo/DbAction.h>
16 
17 #include <Wt/Dbo/Field_impl.h>
18 
19 #ifndef DOXYGEN_ONLY
20 
21 namespace Wt {
22   namespace Dbo {
23     namespace Impl {
24 
25 extern std::string WTDBO_API
26 completeQuerySelectSql(const std::string& sql,
27 		       const std::string& join,
28 		       const std::string& where,
29 		       const std::string& groupBy,
30 		       const std::string& having,
31 		       const std::string& orderBy,
32 		       int limit, int offset,
33 		       const std::vector<FieldInfo>& fields,
34 		       LimitQuery useRowsFromTo);
35 
36 extern std::string WTDBO_API
37 createQuerySelectSql(const std::string& from,
38 		     const std::string& join,
39 		     const std::string& where,
40 		     const std::string& groupBy,
41 		     const std::string& having,
42 		     const std::string& orderBy,
43 		     int limit, int offset,
44 		     const std::vector<FieldInfo>& fields,
45 		     LimitQuery useRowsFromTo);
46 
47 extern std::string WTDBO_API
48 createQueryCountSql(const std::string& query,
49                     bool requireSubqueryAlias);
50 
51 extern void WTDBO_API
52 substituteFields(const SelectFieldList& list,
53 		 const std::vector<FieldInfo>& fs,
54 		 std::string& sql,
55                  int& offset);
56 
57 extern void WTDBO_API
58 parseSql(const std::string& sql, SelectFieldLists& fieldLists);
59 
60 template <class Result>
QueryBase()61 QueryBase<Result>::QueryBase()
62   : session_(nullptr)
63 { }
64 
65 template <class Result>
QueryBase(Session & session,const std::string & sql)66 QueryBase<Result>::QueryBase(Session& session, const std::string& sql)
67   : session_(&session),
68     sql_(sql)
69 {
70   parseSql(sql_, selectFieldLists_);
71 }
72 
73 template <class Result>
QueryBase(Session & session,const std::string & table,const std::string & where)74 QueryBase<Result>::QueryBase(Session& session, const std::string& table,
75 			     const std::string& where)
76   : session_(&session)
77 {
78   sql_ = "from " + table + ' ' + where;
79 }
80 
81 template <class Result>
82 QueryBase<Result>& QueryBase<Result>::operator=(const QueryBase<Result>& other)
83 {
84   session_ = other.session_;
85   sql_ = other.sql_;
86   selectFieldLists_ = other.selectFieldLists_;
87 
88   return *this;
89 }
90 
91 template <class Result>
fields()92 std::vector<FieldInfo> QueryBase<Result>::fields() const
93 {
94   std::vector<FieldInfo> result;
95 
96   if (selectFieldLists_.empty())
97     query_result_traits<Result>::getFields(*session_, 0, result);
98   else {
99     /*
100      * We'll build only the aliases from the first selection list
101      * (this matters only for compound selects
102      */
103     fieldsForSelect(selectFieldLists_[0], result);
104   }
105 
106   return result;
107 }
108 
109 template <class Result>
110 std::pair<SqlStatement *, SqlStatement *>
statements(const std::string & join,const std::string & where,const std::string & groupBy,const std::string & having,const std::string & orderBy,int limit,int offset)111 QueryBase<Result>::statements(const std::string& join,
112 			      const std::string& where,
113 			      const std::string& groupBy,
114 			      const std::string& having,
115 			      const std::string& orderBy,
116 			      int limit, int offset) const
117 {
118   SqlStatement *statement, *countStatement;
119 
120   if (selectFieldLists_.empty()) {
121     /*
122      * sql_ is "from ..."
123      */
124     std::string sql;
125 
126     std::vector<FieldInfo> fs = this->fields();
127     sql = Impl::createQuerySelectSql(sql_, join, where, groupBy, having, orderBy,
128                                      limit, offset, fs,
129                                      this->session_->limitQueryMethod_);
130     statement = this->session_->getOrPrepareStatement(sql);
131 
132     sql = Impl::createQueryCountSql(sql, this->session_->requireSubqueryAlias_);
133 
134     countStatement = this->session_->getOrPrepareStatement(sql);
135   } else {
136     /*
137      * sql_ is complete "[with ...] select ..."
138      */
139     std::string sql = sql_;
140     int sql_offset = 0;
141 
142     std::vector<FieldInfo> fs;
143     for (unsigned i = 0; i < selectFieldLists_.size(); ++i) {
144       const SelectFieldList& list = selectFieldLists_[i];
145 
146       fs.clear();
147       this->fieldsForSelect(list, fs);
148 
149       Impl::substituteFields(list, fs, sql, sql_offset);
150     }
151 
152     sql = Impl::completeQuerySelectSql(sql, join, where, groupBy, having, orderBy,
153                                        limit, offset, fs,
154                                        this->session_->limitQueryMethod_);
155 
156     statement = this->session_->getOrPrepareStatement(sql);
157 
158     sql = Impl::createQueryCountSql(sql, this->session_->requireSubqueryAlias_);
159 
160     countStatement = this->session_->getOrPrepareStatement(sql);
161   }
162 
163   return std::make_pair(statement, countStatement);
164 }
165 
166 template <class Result>
fieldsForSelect(const SelectFieldList & list,std::vector<FieldInfo> & result)167 void QueryBase<Result>::fieldsForSelect(const SelectFieldList& list,
168 					std::vector<FieldInfo>& result) const
169 {
170   std::vector<std::string> aliases;
171   for (unsigned i = 0; i < list.size(); ++i) {
172     const SelectField& field = list[i];
173     aliases.push_back(sql_.substr(field.begin, field.end - field.begin));
174   }
175 
176   query_result_traits<Result>::getFields(*session_, &aliases, result);
177   if (!aliases.empty())
178     throw Exception("Session::query(): too many aliases for result");
179 }
180 
181 template <class Result>
session()182 Session& QueryBase<Result>::session() const
183 {
184   return *session_;
185 }
186 
187 template <class Result>
singleResult(const collection<Result> & results)188 Result QueryBase<Result>::singleResult(const collection<Result>& results) const
189 {
190   typename collection<Result>::const_iterator i = results.begin();
191   if (i == results.end())
192     return Result();
193   else {
194     Result result = *i;
195     ++i;
196     if (i != results.end())
197       throw NoUniqueResultException();
198     return result;
199   }
200 }
201     }
202 
203 template <class Result>
Query()204 Query<Result, DirectBinding>::Query()
205   : statement_(nullptr),
206     countStatement_(nullptr)
207 { }
208 
209 template <class Result>
Query(Session & session,const std::string & sql)210 Query<Result, DirectBinding>::Query(Session& session, const std::string& sql)
211   : Impl::QueryBase<Result>(session, sql),
212     statement_(nullptr),
213     countStatement_(nullptr)
214 {
215   prepareStatements();
216 }
217 
218 template <class Result>
Query(Session & session,const std::string & table,const std::string & where)219 Query<Result, DirectBinding>::Query(Session& session, const std::string& table,
220 				    const std::string& where)
221   : Impl::QueryBase<Result>(session, table, where),
222     statement_(nullptr),
223     countStatement_(nullptr)
224 {
225   prepareStatements();
226 }
227 
228 template <class Result>
~Query()229 Query<Result, DirectBinding>::~Query()
230 {
231   if (statement_)
232     statement_->done();
233   if (countStatement_)
234     countStatement_->done();
235 }
236 
237 template <class Result>
238 template <typename T>
239 Query<Result, DirectBinding>&
bind(const T & value)240 Query<Result, DirectBinding>::bind(const T& value)
241 {
242   sql_value_traits<T>::bind(value, this->statement_, column_, -1);
243   sql_value_traits<T>::bind(value, this->countStatement_, column_, -1);
244 
245   ++column_;
246 
247   return *this;
248 }
249 
250 template <class Result>
reset()251 void Query<Result, DirectBinding>::reset()
252 {
253   column_ = 0;
254   this->statement_->reset();
255   this->countStatement_->reset();
256 }
257 
258 template <class Result>
resultValue()259 Result Query<Result, DirectBinding>::resultValue() const
260 {
261   return this->singleResult(resultList());
262 }
263 
264 template <class Result>
resultList()265 collection<Result> Query<Result, DirectBinding>::resultList() const
266 {
267   if (!this->session_)
268     return collection<Result>();
269 
270   if (!statement_)
271     throw std::logic_error("Query<Result, DirectBinding>::resultList() "
272 			   "may be called only once");
273 
274   SqlStatement *s = this->statement_, *cs = this->countStatement_;
275   this->statement_ = this->countStatement_ = nullptr;
276 
277   return collection<Result>(this->session_, s, cs);
278 }
279 
280 template <class Result>
Result()281 Query<Result, DirectBinding>::operator Result () const
282 {
283   return resultValue();
284 }
285 
286 template <class Result>
287 Query<Result, DirectBinding>::operator collection<Result> () const
288 {
289   return resultList();
290 }
291 
292 template <class Result>
prepareStatements()293 void Query<Result, DirectBinding>::prepareStatements() const
294 {
295   if (!this->session_)
296     return;
297 
298   this->session_->flush();
299 
300   std::tie(this->statement_, this->countStatement_)
301     = this->statements(std::string(), std::string(), std::string(), std::string(),
302 		       std::string(), -1, -1);
303 
304   column_ = 0;
305 }
306 
307 namespace Impl {
308   template <typename T>
bind(SaveBaseAction & binder)309   void Parameter<T>::bind(SaveBaseAction& binder)
310   {
311     field(binder, v_, "parameter");
312   }
313 
314   template <typename T>
clone()315   Parameter<T> *Parameter<T>::clone() const
316   {
317     return new Parameter<T>(v_);
318   }
319 }
320 
321 
322 
323 template <class Result>
Query()324 Query<Result, DynamicBinding>::Query()
325 { }
326 
327 template <class Result>
Query(Session & session,const std::string & sql)328 Query<Result, DynamicBinding>::Query(Session& session, const std::string& sql)
329   : Impl::QueryBase<Result>(session, sql)
330 { }
331 
332 template <class Result>
Query(Session & session,const std::string & table,const std::string & where)333 Query<Result, DynamicBinding>::Query(Session& session,
334 				     const std::string& table,
335 				     const std::string& where)
336   : Impl::QueryBase<Result>(session, table, where)
337 { }
338 
339 template <class Result>
340 Query<Result, DynamicBinding>
Query(const Query<Result,DynamicBinding> & other)341 ::Query(const Query<Result, DynamicBinding>& other)
342   : AbstractQuery(other),
343     Impl::QueryBase<Result>(other)
344 { }
345 
346 template <class Result>
347 Query<Result, DynamicBinding>&
348 Query<Result, DynamicBinding>::operator=
349 (const Query<Result, DynamicBinding>& other)
350 {
351   Impl::QueryBase<Result>::operator=(other);
352   AbstractQuery::operator=(other);
353   return *this;
354 }
355 
356 template <class Result>
~Query()357 Query<Result, DynamicBinding>::~Query()
358 {
359   reset();
360 }
361 
362 template <class Result>
363 Query<Result, DynamicBinding>&
join(const std::string & other)364 Query<Result, DynamicBinding>::join(const std::string& other)
365 {
366   AbstractQuery::join(other);
367 
368   return *this;
369 }
370 
371 template <class Result>
372 Query<Result, DynamicBinding>&
leftJoin(const std::string & other)373 Query<Result, DynamicBinding>::leftJoin(const std::string& other)
374 {
375   AbstractQuery::leftJoin(other);
376 
377   return *this;
378 }
379 
380 template <class Result>
381 Query<Result, DynamicBinding>&
rightJoin(const std::string & other)382 Query<Result, DynamicBinding>::rightJoin(const std::string& other)
383 {
384   AbstractQuery::rightJoin(other);
385 
386   return *this;
387 }
388 
389 template <class Result>
390 Query<Result, DynamicBinding>&
where(const std::string & where)391 Query<Result, DynamicBinding>::where(const std::string& where)
392 {
393   AbstractQuery::where(where);
394 
395   return *this;
396 }
397 
398 template <class Result>
399 Query<Result, DynamicBinding>&
orWhere(const std::string & where)400 Query<Result, DynamicBinding>::orWhere(const std::string& where)
401 {
402   AbstractQuery::orWhere(where);
403 
404   return *this;
405 }
406 
407 template <class Result>
408 Query<Result, DynamicBinding>&
orderBy(const std::string & orderBy)409 Query<Result, DynamicBinding>::orderBy(const std::string& orderBy)
410 {
411   AbstractQuery::orderBy(orderBy);
412 
413   return *this;
414 }
415 
416 template <class Result>
417 Query<Result, DynamicBinding>&
groupBy(const std::string & groupBy)418 Query<Result, DynamicBinding>::groupBy(const std::string& groupBy)
419 {
420   AbstractQuery::groupBy(groupBy);
421 
422   return *this;
423 }
424 
425 template <class Result>
426 Query<Result, DynamicBinding>&
having(const std::string & having)427 Query<Result, DynamicBinding>::having(const std::string& having)
428 {
429   AbstractQuery::having(having);
430 
431   return *this;
432 }
433 template <class Result>
434 Query<Result, DynamicBinding>&
offset(int offset)435 Query<Result, DynamicBinding>::offset(int offset)
436 {
437   AbstractQuery::offset(offset);
438 
439   return *this;
440 }
441 
442 template <class Result>
443 Query<Result, DynamicBinding>&
limit(int limit)444 Query<Result, DynamicBinding>::limit(int limit)
445 {
446   AbstractQuery::limit(limit);
447 
448   return *this;
449 }
450 
451 template <class Result>
resultValue()452 Result Query<Result, DynamicBinding>::resultValue() const
453 {
454   return this->singleResult(resultList());
455 }
456 
457 template <class Result>
resultList()458 collection<Result> Query<Result, DynamicBinding>::resultList() const
459 {
460   if (!this->session_)
461     return collection<Result>();
462 
463   this->session_->flush();
464 
465   SqlStatement *statement, *countStatement;
466 
467   std::tie(statement, countStatement)
468     = this->statements(join_, where_, groupBy_, having_, orderBy_, limit_, offset_);
469 
470   bindParameters(this->session_, statement);
471   bindParameters(this->session_, countStatement);
472 
473   return collection<Result>(this->session_, statement, countStatement);
474 }
475 
476 template <class Result>
Result()477 Query<Result, DynamicBinding>::operator Result () const
478 {
479   return resultValue();
480 }
481 
482 template <class Result>
483 Query<Result, DynamicBinding>::operator collection<Result> () const
484 {
485   return resultList();
486 }
487 
488   }
489 }
490 
491 #endif // DOXYGEN_ONLY
492 
493 #endif // WT_DBO_QUERY_IMPL_H_
494