drogon
C++14/17-based HTTP application framework
Loading...
Searching...
No Matches
CoroMapper.h
Go to the documentation of this file.
1
14#pragma once
15
16#include <functional>
17#include <tuple>
18
19#ifdef __cpp_impl_coroutine
20#include <drogon/orm/Mapper.h>
22
23namespace drogon
24{
25namespace orm
26{
27namespace internal
28{
29template <typename ReturnType>
30struct [[nodiscard]] MapperAwaiter : public CallbackAwaiter<ReturnType>
31{
32 using MapperFunction =
33 std::function<void(std::function<void(ReturnType result)> &&,
34 std::function<void(const std::exception_ptr &)> &&)>;
35
36 explicit MapperAwaiter(MapperFunction &&function)
37 : function_(std::move(function))
38 {
39 }
40
41 void await_suspend(std::coroutine_handle<> handle)
42 {
43 function_(
44 [handle, this](ReturnType result) {
45 this->setValue(std::move(result));
46 handle.resume();
47 },
48 [handle, this](const std::exception_ptr &e) {
49 this->setException(e);
50 handle.resume();
51 });
52 }
53
54 private:
55 MapperFunction function_;
56};
57} // namespace internal
58
66template <typename T>
67class CoroMapper : public Mapper<T>
68{
69 public:
70 using SingleRowCallback = typename Mapper<T>::SingleRowCallback;
71 using MultipleRowsCallback = typename Mapper<T>::MultipleRowsCallback;
72 using CountCallback = typename Mapper<T>::CountCallback;
73 using ExceptPtrCallback = std::function<void(const std::exception_ptr &)>;
74
75 explicit CoroMapper(DbClientPtr client) : Mapper<T>(std::move(client))
76 {
77 }
78
79 using TraitsPKType = typename Mapper<T>::TraitsPKType;
80
81 inline internal::MapperAwaiter<T> findByPrimaryKey(const TraitsPKType &key)
82 {
83 if constexpr (!std::is_same_v<typename T::PrimaryKeyType, void>)
84 {
85 auto lb = [this, key](SingleRowCallback &&callback,
86 ExceptPtrCallback &&errCallback) mutable {
87 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
88 "No primary key in the table!");
89 static_assert(
90 internal::has_sqlForFindingByPrimaryKey<T>::value,
91 "No function member named sqlForFindingByPrimaryKey, "
92 "please "
93 "make sure that the model class is generated by the latest "
94 "version of drogon_ctl");
95 // return findFutureOne(Criteria(T::primaryKeyName, key));
96 std::string sql = T::sqlForFindingByPrimaryKey();
97 if (this->forUpdate_)
98 {
99 sql += " for update";
100 }
101 this->clear();
102 auto binder = *(this->client_) << std::move(sql);
103 this->outputPrimaryKeyToBinder(key, binder);
104
105 binder >> [callback = std::move(callback),
106 errCallback](const Result &r) {
107 if (r.size() == 0)
108 {
109 errCallback(std::make_exception_ptr(
110 UnexpectedRows("0 rows found")));
111 }
112 else if (r.size() > 1)
113 {
114 errCallback(std::make_exception_ptr(
115 UnexpectedRows("Found more than one row")));
116 }
117 else
118 {
119 callback(T(r[0]));
120 }
121 };
122 binder >> std::move(errCallback);
123 binder.exec();
124 };
125 return internal::MapperAwaiter<T>(std::move(lb));
126 }
127 else
128 {
129 LOG_FATAL << "The table must have a primary key";
130 abort();
131 }
132 }
133
134 // Query condition overrides
135
142 CoroMapper<T> &limit(size_t limit)
143 {
144 Mapper<T>::limit(limit);
145 return *this;
146 }
147
154 CoroMapper<T> &offset(size_t offset)
155 {
156 Mapper<T>::offset(offset);
157 return *this;
158 }
159
167 CoroMapper<T> &orderBy(const std::string &colName,
168 const SortOrder &order = SortOrder::ASC)
169 {
170 Mapper<T>::orderBy(colName, order);
171 return *this;
172 }
173
181 CoroMapper<T> &orderBy(size_t colIndex,
182 const SortOrder &order = SortOrder::ASC)
183 {
184 Mapper<T>::orderBy(colIndex, order);
185 return *this;
186 }
187
197 CoroMapper<T> &paginate(size_t page, size_t perPage)
198 {
199 Mapper<T>::paginate(page, perPage);
200 return *this;
201 }
202
208 CoroMapper<T> &forUpdate()
209 {
210 Mapper<T>::forUpdate();
211 return *this;
212 }
213
222 CoroMapper<T> &innerJoin(const std::string &table,
223 const std::string &onLeft,
224 const std::string &onRight)
225 {
226 Mapper<T>::innerJoin(table, onLeft, onRight);
227 return *this;
228 }
229
238 CoroMapper<T> &leftJoin(const std::string &table,
239 const std::string &onLeft,
240 const std::string &onRight)
241 {
242 Mapper<T>::leftJoin(table, onLeft, onRight);
243 return *this;
244 }
245
254 CoroMapper<T> &rightJoin(const std::string &table,
255 const std::string &onLeft,
256 const std::string &onRight)
257 {
258 Mapper<T>::rightJoin(table, onLeft, onRight);
259 return *this;
260 }
261
262 // Read api for coroutines
263
264 inline internal::MapperAwaiter<std::vector<T>> findAll()
265 {
266 return findBy(Criteria());
267 }
268
269 inline internal::MapperAwaiter<size_t> count(
270 const Criteria &criteria = Criteria())
271 {
272 auto lb = [this, criteria](CountCallback &&callback,
273 ExceptPtrCallback &&errCallback) {
274 std::string sql = "select count(*) from ";
275 sql += T::tableName;
276 sql += this->joinString_;
277 if (criteria)
278 {
279 sql += " where ";
280 sql += criteria.criteriaString();
281 sql = this->replaceSqlPlaceHolder(sql, "$?");
282 }
283 this->clear();
284 auto binder = *(this->client_) << std::move(sql);
285 if (criteria)
286 criteria.outputArgs(binder);
287 binder >> [callback = std::move(callback)](const Result &r) {
288 assert(r.size() == 1);
289 callback(r[0][(Row::SizeType)0].as<size_t>());
290 };
291 binder >> std::move(errCallback);
292 };
293 return internal::MapperAwaiter<size_t>(std::move(lb));
294 }
295
296 inline internal::MapperAwaiter<T> findOne(const Criteria &criteria)
297 {
298 auto lb = [this, criteria](SingleRowCallback &&callback,
299 ExceptPtrCallback &&errCallback) {
300 std::string sql = "select * from ";
301 sql += T::tableName;
302 sql += this->joinString_;
303 bool hasParameters = false;
304 if (criteria)
305 {
306 sql += " where ";
307 sql += criteria.criteriaString();
308 hasParameters = true;
309 }
310 sql.append(this->orderByString_);
311 if (this->limit_ > 0)
312 {
313 hasParameters = true;
314 sql.append(" limit $?");
315 }
316 if (this->offset_ > 0)
317 {
318 hasParameters = true;
319 sql.append(" offset $?");
320 }
321 if (hasParameters)
322 sql = this->replaceSqlPlaceHolder(sql, "$?");
323 if (this->forUpdate_)
324 {
325 sql += " for update";
326 }
327 auto binder = *(this->client_) << std::move(sql);
328 if (criteria)
329 criteria.outputArgs(binder);
330 if (this->limit_ > 0)
331 binder << this->limit_;
332 if (this->offset_)
333 binder << this->offset_;
334 this->clear();
335 binder >>
336 [errCallback, callback = std::move(callback)](const Result &r) {
337 if (r.size() == 0)
338 {
339 errCallback(std::make_exception_ptr(
340 UnexpectedRows("0 rows found")));
341 }
342 else if (r.size() > 1)
343 {
344 errCallback(std::make_exception_ptr(
345 UnexpectedRows("Found more than one row")));
346 }
347 else
348 {
349 callback(T(r[0]));
350 }
351 };
352 binder >> std::move(errCallback);
353 };
354 return internal::MapperAwaiter<T>(std::move(lb));
355 }
356
357 inline internal::MapperAwaiter<std::vector<T>> findBy(
358 const Criteria &criteria)
359 {
360 auto lb = [this, criteria](MultipleRowsCallback &&callback,
361 ExceptPtrCallback &&errCallback) {
362 std::string sql = "select * from ";
363 sql += T::tableName;
364 sql += this->joinString_;
365 bool hasParameters = false;
366 if (criteria)
367 {
368 hasParameters = true;
369 sql += " where ";
370 sql += criteria.criteriaString();
371 }
372 sql.append(this->orderByString_);
373 if (this->limit_ > 0)
374 {
375 hasParameters = true;
376 sql.append(" limit $?");
377 }
378 if (this->offset_ > 0)
379 {
380 hasParameters = true;
381 sql.append(" offset $?");
382 }
383 if (hasParameters)
384 sql = this->replaceSqlPlaceHolder(sql, "$?");
385 if (this->forUpdate_)
386 {
387 sql += " for update";
388 }
389 auto binder = *(this->client_) << std::move(sql);
390 if (criteria)
391 criteria.outputArgs(binder);
392 if (this->limit_ > 0)
393 binder << this->limit_;
394 if (this->offset_)
395 binder << this->offset_;
396 this->clear();
397 binder >> [callback = std::move(callback)](const Result &r) {
398 std::vector<T> ret;
399 for (auto const &row : r)
400 {
401 ret.push_back(T(row));
402 }
403 callback(ret);
404 };
405 binder >> std::move(errCallback);
406 };
407 return internal::MapperAwaiter<std::vector<T>>(std::move(lb));
408 }
409
410 inline internal::MapperAwaiter<T> insert(const T &obj)
411 {
412 auto lb = [this, obj](SingleRowCallback &&callback,
413 ExceptPtrCallback &&errCallback) {
414 this->clear();
415 bool needSelection = false;
416 auto binder = *(this->client_)
417 << obj.sqlForInserting(needSelection);
418 obj.outputArgs(binder);
419 auto client = this->client_;
420 binder >> [client,
421 callback = std::move(callback),
422 obj,
423 needSelection,
424 errCallback](const Result &r) {
425 assert(r.affectedRows() == 1);
426 if (client->type() == ClientType::PostgreSQL)
427 {
428 if (needSelection)
429 {
430 assert(r.size() == 1);
431 callback(T(r[0]));
432 }
433 else
434 {
435 callback(obj);
436 }
437 }
438 else // Mysql or Sqlite3
439 {
440 auto id = r.insertId();
441 auto newObj = obj;
442 newObj.updateId(id);
443 if (needSelection)
444 {
445 auto tmp = Mapper<T>(client);
446 tmp.findByPrimaryKey(
447 newObj.getPrimaryKey(),
448 callback,
449 [errCallback](const DrogonDbException &err) {
450 errCallback(std::make_exception_ptr(
451 Failure(err.base().what())));
452 });
453 }
454 else
455 {
456 callback(newObj);
457 }
458 }
459 };
460 binder >> std::move(errCallback);
461 };
462 return internal::MapperAwaiter<T>(std::move(lb));
463 }
464
465 inline internal::MapperAwaiter<size_t> update(const T &obj)
466 {
467 auto lb = [this, obj](CountCallback &&callback,
468 ExceptPtrCallback &&errCallback) {
469 this->clear();
470 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
471 "No primary key in the table!");
472 std::vector<std::string> colNames = obj.updateColumns();
473 if (colNames.empty())
474 {
475 callback(0);
476 return;
477 }
478 std::string sql = "update ";
479 sql += T::tableName;
480 sql += " set ";
481 for (auto const &colName : colNames)
482 {
483 sql += colName;
484 sql += " = $?,";
485 }
486 sql[sql.length() - 1] = ' '; // Replace the last ','
487
488 this->makePrimaryKeyCriteria(sql);
489
490 sql = this->replaceSqlPlaceHolder(sql, "$?");
491 auto binder = *(this->client_) << std::move(sql);
492 obj.updateArgs(binder);
493 this->outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);
494 binder >> [callback = std::move(callback)](const Result &r) {
495 callback(r.affectedRows());
496 };
497 binder >> std::move(errCallback);
498 };
499 return internal::MapperAwaiter<size_t>(std::move(lb));
500 }
501
502 template <typename... TupleArgs, typename... Arguments>
503 inline internal::MapperAwaiter<size_t> updateBy(
504 const std::tuple<TupleArgs...> &colNames,
505 const Criteria &criteria,
506 Arguments &&...args)
507 {
508 static_assert(sizeof...(args) > 0);
509 static_assert(sizeof...(args) ==
510 std::tuple_size_v<std::tuple<TupleArgs...>>);
511 std::string sql = "update ";
512 sql += T::tableName;
513 sql += " set ";
514 std::apply(
515 [&sql](auto &&...name) {
516 ((sql += std::string(name) + " = $?,"), ...);
517 },
518 colNames);
519 sql[sql.length() - 1] = ' '; // Replace the last ','
520
521 return updateByHelper(std::move(sql),
522 criteria,
523 std::forward<Arguments>(args)...);
524 }
525
526 template <typename... Arguments>
527 internal::MapperAwaiter<size_t> updateBy(
528 const std::vector<std::string> &colNames,
529 const Criteria &criteria,
530 Arguments &&...args)
531 {
532 static_assert(sizeof...(args) > 0);
533 assert(colNames.size() == sizeof...(args));
534 std::string sql = "update ";
535 sql += T::tableName;
536 sql += " set ";
537 for (auto const &colName : colNames)
538 {
539 sql += colName;
540 sql += " = $?,";
541 }
542 sql[sql.length() - 1] = ' '; // Replace the last ','
543
544 return updateByHelper(std::move(sql),
545 criteria,
546 std::forward<Arguments>(args)...);
547 }
548
549 template <typename... Arguments>
550 inline internal::MapperAwaiter<size_t> increment(
551 const std::vector<std::string> &colNames,
552 const Criteria &criteria,
553 Arguments... args)
554 {
555 static_assert(sizeof...(args) > 0);
556 assert(colNames.size() == sizeof...(args));
557 std::string sql = "update ";
558 sql += T::tableName;
559 sql += " set ";
560
561 std::vector<const char *> temps;
562 (void)std::initializer_list<int>{(
563 [&args, &temps] {
564 args = (args < 0) ? (temps.push_back(" - $?,"), -args)
565 : (temps.push_back(" + $?,"), args);
566 }(),
567 0)...};
568
569 for (int i = 0; i < sizeof...(args); ++i)
570 {
571 const auto &colName = colNames[i];
572 sql += colName;
573 sql += " = ";
574 sql += colName;
575 sql += temps[i];
576 }
577 sql[sql.length() - 1] = ' '; // Replace the last ','
578
579 return updateByHelper(std::move(sql),
580 criteria,
581 std::forward<Arguments>(args)...);
582 }
583
584 private:
585 template <typename... Arguments>
586 internal::MapperAwaiter<size_t> updateByHelper(std::string &&sql,
587 const Criteria &criteria,
588 Arguments &&...args)
589 {
590 auto lb = [this,
591 sql = std::move(sql),
592 criteria,
593 ... args = std::forward<Arguments>(
594 args)](CountCallback &&callback,
595 ExceptPtrCallback &&errCallback) mutable {
596 this->clear();
597
598 if (criteria)
599 {
600 sql += " where ";
601 sql += criteria.criteriaString();
602 }
603
604 sql = this->replaceSqlPlaceHolder(sql, "$?");
605 auto binder = *(this->client_) << std::move(sql);
606 (void)std::initializer_list<int>{(binder << args, 0)...};
607 if (criteria)
608 criteria.outputArgs(binder);
609 binder >> [callback = std::move(callback)](const Result &r) {
610 callback(r.affectedRows());
611 };
612 binder >> std::move(errCallback);
613 };
614 return internal::MapperAwaiter<size_t>(std::move(lb));
615 }
616
617 public:
618 inline internal::MapperAwaiter<size_t> deleteOne(const T &obj)
619 {
620 auto lb = [this, obj](CountCallback &&callback,
621 ExceptPtrCallback &&errCallback) {
622 this->clear();
623 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
624 "No primary key in the table!");
625 std::string sql = "delete from ";
626 sql += T::tableName;
627 sql += " ";
628
629 this->makePrimaryKeyCriteria(sql);
630
631 sql = this->replaceSqlPlaceHolder(sql, "$?");
632 auto binder = *(this->client_) << std::move(sql);
633 this->outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);
634 binder >> [callback = std::move(callback)](const Result &r) {
635 callback(r.affectedRows());
636 };
637 binder >> std::move(errCallback);
638 };
639 return internal::MapperAwaiter<size_t>(std::move(lb));
640 }
641
642 inline internal::MapperAwaiter<size_t> deleteBy(const Criteria &criteria)
643 {
644 auto lb = [this, criteria](CountCallback &&callback,
645 ExceptPtrCallback &&errCallback) {
646 this->clear();
647 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
648 "No primary key in the table!");
649 std::string sql = "delete from ";
650 sql += T::tableName;
651
652 if (criteria)
653 {
654 sql += " where ";
655 sql += criteria.criteriaString();
656 sql = this->replaceSqlPlaceHolder(sql, "$?");
657 }
658
659 auto binder = *(this->client_) << std::move(sql);
660 if (criteria)
661 {
662 criteria.outputArgs(binder);
663 }
664 binder >> [callback = std::move(callback)](const Result &r) {
665 callback(r.affectedRows());
666 };
667 binder >> std::move(errCallback);
668 };
669 return internal::MapperAwaiter<size_t>(std::move(lb));
670 }
671
672 inline internal::MapperAwaiter<size_t> deleteByPrimaryKey(
673 const TraitsPKType &key)
674 {
675 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
676 "No primary key in the table!");
677 static_assert(
678 internal::has_sqlForDeletingByPrimaryKey<T>::value,
679 "No function member named sqlForDeletingByPrimaryKey, please "
680 "make sure that the model class is generated by the latest "
681 "version of drogon_ctl");
682 auto lb = [this, key](CountCallback &&callback,
683 ExceptPtrCallback &&errCallback) {
684 this->clear();
685 auto binder = *(this->client_) << T::sqlForDeletingByPrimaryKey();
686 this->outputPrimaryKeyToBinder(key, binder);
687 binder >> [callback = std::move(callback)](const Result &r) {
688 callback(r.affectedRows());
689 };
690 binder >> std::move(errCallback);
691 };
692 return internal::MapperAwaiter<size_t>(std::move(lb));
693 }
694};
695} // namespace orm
696} // namespace drogon
697#endif
The mapper template.
Definition Mapper.h:117
Drogon Test is a minimal effort test framework developed because the major C++ test frameworks doesn'...
Definition Attribute.h:23