19#ifdef __cpp_impl_coroutine
29template <
typename ReturnType>
30struct [[nodiscard]] MapperAwaiter :
public CallbackAwaiter<ReturnType>
32 using MapperFunction =
33 std::function<void(std::function<
void(ReturnType result)> &&,
34 std::function<
void(
const std::exception_ptr &)> &&)>;
36 explicit MapperAwaiter(MapperFunction &&function)
37 : function_(std::move(function))
41 void await_suspend(std::coroutine_handle<> handle)
44 [handle,
this](ReturnType result) {
45 this->setValue(std::move(result));
48 [handle,
this](
const std::exception_ptr &e) {
49 this->setException(e);
55 MapperFunction function_;
67class CoroMapper :
public Mapper<T>
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 &)>;
75 explicit CoroMapper(DbClientPtr client) : Mapper<T>(std::move(client))
79 using TraitsPKType =
typename Mapper<T>::TraitsPKType;
81 inline internal::MapperAwaiter<T> findByPrimaryKey(
const TraitsPKType &key)
83 if constexpr (!std::is_same_v<typename T::PrimaryKeyType, void>)
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!");
90 internal::has_sqlForFindingByPrimaryKey<T>::value,
91 "No function member named sqlForFindingByPrimaryKey, "
93 "make sure that the model class is generated by the latest "
94 "version of drogon_ctl");
96 std::string sql = T::sqlForFindingByPrimaryKey();
102 auto binder = *(this->client_) << std::move(sql);
103 this->outputPrimaryKeyToBinder(key, binder);
105 binder >> [callback = std::move(callback),
106 errCallback](
const Result &r) {
109 errCallback(std::make_exception_ptr(
110 UnexpectedRows(
"0 rows found")));
112 else if (r.size() > 1)
114 errCallback(std::make_exception_ptr(
115 UnexpectedRows(
"Found more than one row")));
122 binder >> std::move(errCallback);
125 return internal::MapperAwaiter<T>(std::move(lb));
129 LOG_FATAL <<
"The table must have a primary key";
142 CoroMapper<T> &limit(
size_t limit)
144 Mapper<T>::limit(limit);
154 CoroMapper<T> &offset(
size_t offset)
156 Mapper<T>::offset(offset);
167 CoroMapper<T> &orderBy(
const std::string &colName,
168 const SortOrder &order = SortOrder::ASC)
170 Mapper<T>::orderBy(colName, order);
181 CoroMapper<T> &orderBy(
size_t colIndex,
182 const SortOrder &order = SortOrder::ASC)
184 Mapper<T>::orderBy(colIndex, order);
197 CoroMapper<T> &paginate(
size_t page,
size_t perPage)
199 Mapper<T>::paginate(page, perPage);
208 CoroMapper<T> &forUpdate()
210 Mapper<T>::forUpdate();
222 CoroMapper<T> &innerJoin(
const std::string &table,
223 const std::string &onLeft,
224 const std::string &onRight)
226 Mapper<T>::innerJoin(table, onLeft, onRight);
238 CoroMapper<T> &leftJoin(
const std::string &table,
239 const std::string &onLeft,
240 const std::string &onRight)
242 Mapper<T>::leftJoin(table, onLeft, onRight);
254 CoroMapper<T> &rightJoin(
const std::string &table,
255 const std::string &onLeft,
256 const std::string &onRight)
258 Mapper<T>::rightJoin(table, onLeft, onRight);
264 inline internal::MapperAwaiter<std::vector<T>> findAll()
266 return findBy(Criteria());
269 inline internal::MapperAwaiter<size_t> count(
270 const Criteria &criteria = Criteria())
272 auto lb = [
this, criteria](CountCallback &&callback,
273 ExceptPtrCallback &&errCallback) {
274 std::string sql =
"select count(*) from ";
276 sql += this->joinString_;
280 sql += criteria.criteriaString();
281 sql = this->replaceSqlPlaceHolder(sql,
"$?");
284 auto binder = *(this->client_) << std::move(sql);
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>());
291 binder >> std::move(errCallback);
293 return internal::MapperAwaiter<size_t>(std::move(lb));
296 inline internal::MapperAwaiter<T> findOne(
const Criteria &criteria)
298 auto lb = [
this, criteria](SingleRowCallback &&callback,
299 ExceptPtrCallback &&errCallback) {
300 std::string sql =
"select * from ";
302 sql += this->joinString_;
303 bool hasParameters =
false;
307 sql += criteria.criteriaString();
308 hasParameters =
true;
310 sql.append(this->orderByString_);
311 if (this->limit_ > 0)
313 hasParameters =
true;
314 sql.append(
" limit $?");
316 if (this->offset_ > 0)
318 hasParameters =
true;
319 sql.append(
" offset $?");
322 sql = this->replaceSqlPlaceHolder(sql,
"$?");
323 if (this->forUpdate_)
325 sql +=
" for update";
327 auto binder = *(this->client_) << std::move(sql);
329 criteria.outputArgs(binder);
330 if (this->limit_ > 0)
331 binder << this->limit_;
333 binder << this->offset_;
336 [errCallback, callback = std::move(callback)](
const Result &r) {
339 errCallback(std::make_exception_ptr(
340 UnexpectedRows(
"0 rows found")));
342 else if (r.size() > 1)
344 errCallback(std::make_exception_ptr(
345 UnexpectedRows(
"Found more than one row")));
352 binder >> std::move(errCallback);
354 return internal::MapperAwaiter<T>(std::move(lb));
357 inline internal::MapperAwaiter<std::vector<T>> findBy(
358 const Criteria &criteria)
360 auto lb = [
this, criteria](MultipleRowsCallback &&callback,
361 ExceptPtrCallback &&errCallback) {
362 std::string sql =
"select * from ";
364 sql += this->joinString_;
365 bool hasParameters =
false;
368 hasParameters =
true;
370 sql += criteria.criteriaString();
372 sql.append(this->orderByString_);
373 if (this->limit_ > 0)
375 hasParameters =
true;
376 sql.append(
" limit $?");
378 if (this->offset_ > 0)
380 hasParameters =
true;
381 sql.append(
" offset $?");
384 sql = this->replaceSqlPlaceHolder(sql,
"$?");
385 if (this->forUpdate_)
387 sql +=
" for update";
389 auto binder = *(this->client_) << std::move(sql);
391 criteria.outputArgs(binder);
392 if (this->limit_ > 0)
393 binder << this->limit_;
395 binder << this->offset_;
397 binder >> [callback = std::move(callback)](
const Result &r) {
399 for (
auto const &row : r)
401 ret.push_back(T(row));
405 binder >> std::move(errCallback);
407 return internal::MapperAwaiter<std::vector<T>>(std::move(lb));
410 inline internal::MapperAwaiter<T> insert(
const T &obj)
412 auto lb = [
this, obj](SingleRowCallback &&callback,
413 ExceptPtrCallback &&errCallback) {
415 bool needSelection =
false;
416 auto binder = *(this->client_)
417 << obj.sqlForInserting(needSelection);
418 obj.outputArgs(binder);
419 auto client = this->client_;
421 callback = std::move(callback),
424 errCallback](
const Result &r) {
425 assert(r.affectedRows() == 1);
426 if (client->type() == ClientType::PostgreSQL)
430 assert(r.size() == 1);
440 auto id = r.insertId();
445 auto tmp = Mapper<T>(client);
446 tmp.findByPrimaryKey(
447 newObj.getPrimaryKey(),
449 [errCallback](
const DrogonDbException &err) {
450 errCallback(std::make_exception_ptr(
451 Failure(err.base().what())));
460 binder >> std::move(errCallback);
462 return internal::MapperAwaiter<T>(std::move(lb));
465 inline internal::MapperAwaiter<size_t> update(
const T &obj)
467 auto lb = [
this, obj](CountCallback &&callback,
468 ExceptPtrCallback &&errCallback) {
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())
478 std::string sql =
"update ";
481 for (
auto const &colName : colNames)
486 sql[sql.length() - 1] =
' ';
488 this->makePrimaryKeyCriteria(sql);
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());
497 binder >> std::move(errCallback);
499 return internal::MapperAwaiter<size_t>(std::move(lb));
502 template <
typename... TupleArgs,
typename... Arguments>
503 inline internal::MapperAwaiter<size_t> updateBy(
504 const std::tuple<TupleArgs...> &colNames,
505 const Criteria &criteria,
508 static_assert(
sizeof...(args) > 0);
509 static_assert(
sizeof...(args) ==
510 std::tuple_size_v<std::tuple<TupleArgs...>>);
511 std::string sql =
"update ";
515 [&sql](
auto &&...name) {
516 ((sql += std::string(name) +
" = $?,"), ...);
519 sql[sql.length() - 1] =
' ';
521 return updateByHelper(std::move(sql),
523 std::forward<Arguments>(args)...);
526 template <
typename... Arguments>
527 internal::MapperAwaiter<size_t> updateBy(
528 const std::vector<std::string> &colNames,
529 const Criteria &criteria,
532 static_assert(
sizeof...(args) > 0);
533 assert(colNames.size() ==
sizeof...(args));
534 std::string sql =
"update ";
537 for (
auto const &colName : colNames)
542 sql[sql.length() - 1] =
' ';
544 return updateByHelper(std::move(sql),
546 std::forward<Arguments>(args)...);
549 template <
typename... Arguments>
550 inline internal::MapperAwaiter<size_t> increment(
551 const std::vector<std::string> &colNames,
552 const Criteria &criteria,
555 static_assert(
sizeof...(args) > 0);
556 assert(colNames.size() ==
sizeof...(args));
557 std::string sql =
"update ";
561 std::vector<const char *> temps;
562 (void)std::initializer_list<int>{(
564 args = (args < 0) ? (temps.push_back(
" - $?,"), -args)
565 : (temps.push_back(
" + $?,"), args);
569 for (
int i = 0; i <
sizeof...(args); ++i)
571 const auto &colName = colNames[i];
577 sql[sql.length() - 1] =
' ';
579 return updateByHelper(std::move(sql),
581 std::forward<Arguments>(args)...);
585 template <
typename... Arguments>
586 internal::MapperAwaiter<size_t> updateByHelper(std::string &&sql,
587 const Criteria &criteria,
591 sql = std::move(sql),
593 ... args = std::forward<Arguments>(
594 args)](CountCallback &&callback,
595 ExceptPtrCallback &&errCallback)
mutable {
601 sql += criteria.criteriaString();
604 sql = this->replaceSqlPlaceHolder(sql,
"$?");
605 auto binder = *(this->client_) << std::move(sql);
606 (void)std::initializer_list<int>{(binder << args, 0)...};
608 criteria.outputArgs(binder);
609 binder >> [callback = std::move(callback)](
const Result &r) {
610 callback(r.affectedRows());
612 binder >> std::move(errCallback);
614 return internal::MapperAwaiter<size_t>(std::move(lb));
618 inline internal::MapperAwaiter<size_t> deleteOne(
const T &obj)
620 auto lb = [
this, obj](CountCallback &&callback,
621 ExceptPtrCallback &&errCallback) {
623 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
624 "No primary key in the table!");
625 std::string sql =
"delete from ";
629 this->makePrimaryKeyCriteria(sql);
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());
637 binder >> std::move(errCallback);
639 return internal::MapperAwaiter<size_t>(std::move(lb));
642 inline internal::MapperAwaiter<size_t> deleteBy(
const Criteria &criteria)
644 auto lb = [
this, criteria](CountCallback &&callback,
645 ExceptPtrCallback &&errCallback) {
647 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
648 "No primary key in the table!");
649 std::string sql =
"delete from ";
655 sql += criteria.criteriaString();
656 sql = this->replaceSqlPlaceHolder(sql,
"$?");
659 auto binder = *(this->client_) << std::move(sql);
662 criteria.outputArgs(binder);
664 binder >> [callback = std::move(callback)](
const Result &r) {
665 callback(r.affectedRows());
667 binder >> std::move(errCallback);
669 return internal::MapperAwaiter<size_t>(std::move(lb));
672 inline internal::MapperAwaiter<size_t> deleteByPrimaryKey(
673 const TraitsPKType &key)
675 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
676 "No primary key in the table!");
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) {
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());
690 binder >> std::move(errCallback);
692 return internal::MapperAwaiter<size_t>(std::move(lb));
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