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