Add drogon::orm::CoroMapper<T> template (#712)
Co-authored-by: marty1885 <marty188586@gmail.com>
This commit is contained in:
parent
cfb71cc619
commit
3b8b63d17d
|
@ -13,6 +13,8 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <drogon/utils/optional.h>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <algorithm>
|
||||
#include <coroutine>
|
||||
#include <exception>
|
||||
|
@ -21,7 +23,6 @@
|
|||
#include <atomic>
|
||||
#include <future>
|
||||
#include <cassert>
|
||||
#include <drogon/utils/optional.h>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
|
@ -80,7 +81,7 @@ template <typename T>
|
|||
constexpr bool is_awaitable_v = is_awaitable<T>::value;
|
||||
|
||||
template <typename T>
|
||||
struct final_awiter
|
||||
struct final_awaiter
|
||||
{
|
||||
bool await_ready() noexcept
|
||||
{
|
||||
|
@ -140,7 +141,7 @@ struct Task
|
|||
|
||||
auto final_suspend() noexcept
|
||||
{
|
||||
return final_awiter<promise_type>{};
|
||||
return final_awaiter<promise_type>{};
|
||||
}
|
||||
|
||||
void unhandled_exception()
|
||||
|
@ -247,7 +248,7 @@ struct Task<void>
|
|||
}
|
||||
auto final_suspend() noexcept
|
||||
{
|
||||
return final_awiter<promise_type>{};
|
||||
return final_awaiter<promise_type>{};
|
||||
}
|
||||
void unhandled_exception()
|
||||
{
|
||||
|
@ -349,7 +350,7 @@ struct AsyncTask final
|
|||
/// Helper class that provices the infrastructure for turning callback into
|
||||
/// corourines
|
||||
// The user is responsible to fill in `await_suspend()` and construtors.
|
||||
template <typename T>
|
||||
template <typename T = void>
|
||||
struct CallbackAwaiter
|
||||
{
|
||||
bool await_ready() noexcept
|
||||
|
@ -374,7 +375,7 @@ struct CallbackAwaiter
|
|||
// entire struct to be constructed for awaiting. std::optional takes care of
|
||||
// that.
|
||||
optional<T> result_;
|
||||
std::exception_ptr exception_ = nullptr;
|
||||
std::exception_ptr exception_{nullptr};
|
||||
|
||||
protected:
|
||||
void setException(const std::exception_ptr &e)
|
||||
|
@ -391,6 +392,30 @@ struct CallbackAwaiter
|
|||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CallbackAwaiter<void>
|
||||
{
|
||||
bool await_ready() noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void await_resume() noexcept(false)
|
||||
{
|
||||
if (exception_)
|
||||
std::rethrow_exception(exception_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::exception_ptr exception_{nullptr};
|
||||
|
||||
protected:
|
||||
void setException(const std::exception_ptr &e)
|
||||
{
|
||||
exception_ = e;
|
||||
}
|
||||
};
|
||||
|
||||
// An ok implementation of sync_await. This allows one to call
|
||||
// coroutines and wait for the result from a function.
|
||||
//
|
||||
|
@ -479,5 +504,42 @@ inline auto co_future(Await await) noexcept
|
|||
}(std::move(prom), std::move(await));
|
||||
return fut;
|
||||
}
|
||||
namespace internal
|
||||
{
|
||||
struct TimerAwaiter : CallbackAwaiter<void>
|
||||
{
|
||||
TimerAwaiter(trantor::EventLoop *loop,
|
||||
const std::chrono::duration<long double> &delay)
|
||||
: loop_(loop), delay_(delay.count())
|
||||
{
|
||||
}
|
||||
TimerAwaiter(trantor::EventLoop *loop, double delay)
|
||||
: loop_(loop), delay_(delay)
|
||||
{
|
||||
}
|
||||
void await_suspend(std::coroutine_handle<> handle)
|
||||
{
|
||||
loop_->runAfter(delay_, [handle]() { handle.resume(); });
|
||||
}
|
||||
|
||||
private:
|
||||
trantor::EventLoop *loop_;
|
||||
double delay_;
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
inline Task<void> sleepCoro(
|
||||
trantor::EventLoop *loop,
|
||||
const std::chrono::duration<long double> &delay) noexcept
|
||||
{
|
||||
assert(loop);
|
||||
co_return co_await internal::TimerAwaiter(loop, delay);
|
||||
}
|
||||
|
||||
inline Task<void> sleepCoro(trantor::EventLoop *loop, double delay) noexcept
|
||||
{
|
||||
assert(loop);
|
||||
co_return co_await internal::TimerAwaiter(loop, delay);
|
||||
}
|
||||
|
||||
} // namespace drogon
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
/**
|
||||
*
|
||||
* @file CoroMapper.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef __cpp_impl_coroutine
|
||||
#include <drogon/utils/coroutine.h>
|
||||
#include <drogon/orm/Mapper.h>
|
||||
namespace drogon
|
||||
{
|
||||
namespace orm
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
template <typename ReturnType>
|
||||
struct MapperAwaiter : public CallbackAwaiter<ReturnType>
|
||||
{
|
||||
using MapperFunction =
|
||||
std::function<void(std::function<void(ReturnType result)> &&,
|
||||
std::function<void(const DrogonDbException &)> &&)>;
|
||||
MapperAwaiter(MapperFunction &&function) : function_(std::move(function))
|
||||
{
|
||||
}
|
||||
void await_suspend(std::coroutine_handle<> handle)
|
||||
{
|
||||
function_(
|
||||
[handle, this](ReturnType result) {
|
||||
this->setValue(std::move(result));
|
||||
handle.resume();
|
||||
},
|
||||
[handle, this](const DrogonDbException &e) {
|
||||
this->setException(std::make_exception_ptr(e));
|
||||
handle.resume();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
MapperFunction function_;
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* @brief This template implements coroutine interfaces of ORM. All the methods
|
||||
* of this template are coroutine versions of the synchronous interfaces of the
|
||||
* orm::Mapper template.
|
||||
*
|
||||
* @tparam T The type of the model.
|
||||
*/
|
||||
template <typename T>
|
||||
class CoroMapper : public Mapper<T>
|
||||
{
|
||||
public:
|
||||
CoroMapper(const DbClientPtr &client) : Mapper<T>(client)
|
||||
{
|
||||
}
|
||||
using TraitsPKType = typename Mapper<T>::TraitsPKType;
|
||||
inline const Task<T> findByPrimaryKey(const TraitsPKType &key)
|
||||
{
|
||||
auto lb =
|
||||
[this, key](
|
||||
std::function<void(T)> &&callback,
|
||||
std::function<void(const DrogonDbException &)> &&errCallback) {
|
||||
Mapper<T>::findByPrimaryKey(key,
|
||||
std::move(callback),
|
||||
std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<T>(std::move(lb));
|
||||
}
|
||||
inline const Task<std::vector<T>> findAll()
|
||||
{
|
||||
auto lb =
|
||||
[this](
|
||||
std::function<void(std::vector<T>)> &&callback,
|
||||
std::function<void(const DrogonDbException &)> &&errCallback) {
|
||||
Mapper<T>::findAll(std::move(callback), std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<std::vector<T>>(
|
||||
std::move(lb));
|
||||
}
|
||||
inline const Task<size_t> count(const Criteria &criteria = Criteria())
|
||||
{
|
||||
auto lb =
|
||||
[this, criteria](
|
||||
std::function<void(const size_t)> &&callback,
|
||||
std::function<void(const DrogonDbException &)> &&errCallback) {
|
||||
Mapper<T>::count(criteria,
|
||||
std::move(callback),
|
||||
std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<size_t>(std::move(lb));
|
||||
}
|
||||
inline const Task<T> findOne(const Criteria &criteria)
|
||||
{
|
||||
auto lb =
|
||||
[this, criteria](
|
||||
std::function<void(T)> &&callback,
|
||||
std::function<void(const DrogonDbException &)> &&errCallback) {
|
||||
Mapper<T>::findOne(criteria,
|
||||
std::move(callback),
|
||||
std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<T>(std::move(lb));
|
||||
}
|
||||
inline const Task<std::vector<T>> findBy(const Criteria &criteria)
|
||||
{
|
||||
auto lb =
|
||||
[this, criteria](
|
||||
std::function<void(std::vector<T>)> &&callback,
|
||||
std::function<void(const DrogonDbException &)> &&errCallback) {
|
||||
Mapper<T>::findBy(criteria,
|
||||
std::move(callback),
|
||||
std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<std::vector<T>>(
|
||||
std::move(lb));
|
||||
}
|
||||
inline const Task<T> insert(const T &obj)
|
||||
{
|
||||
auto lb = [this, obj](std::function<void(T)> &&callback,
|
||||
std::function<void(const DrogonDbException &)>
|
||||
&&errCallback) {
|
||||
Mapper<T>::insert(obj, std::move(callback), std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<T>(std::move(lb));
|
||||
}
|
||||
inline const Task<size_t> update(const T &obj)
|
||||
{
|
||||
auto lb = [this, obj](std::function<void(const size_t)> &&callback,
|
||||
std::function<void(const DrogonDbException &)>
|
||||
&&errCallback) {
|
||||
Mapper<T>::update(obj, std::move(callback), std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<size_t>(std::move(lb));
|
||||
}
|
||||
inline const Task<size_t> deleteOne(const T &obj)
|
||||
{
|
||||
auto lb =
|
||||
[this, obj](
|
||||
std::function<void(const size_t)> &&callback,
|
||||
std::function<void(const DrogonDbException &)> &&errCallback) {
|
||||
Mapper<T>::deleteOne(obj,
|
||||
std::move(callback),
|
||||
std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<size_t>(std::move(lb));
|
||||
}
|
||||
inline const Task<size_t> deleteBy(const Criteria &criteria)
|
||||
{
|
||||
auto lb =
|
||||
[this, criteria](
|
||||
std::function<void(const size_t)> &&callback,
|
||||
std::function<void(const DrogonDbException &)> &&errCallback) {
|
||||
Mapper<T>::deleteBy(criteria,
|
||||
std::move(callback),
|
||||
std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<size_t>(std::move(lb));
|
||||
}
|
||||
inline const Task<size_t> deleteByPrimaryKey(const TraitsPKType &key)
|
||||
{
|
||||
auto lb =
|
||||
[this, key](
|
||||
std::function<void(const size_t)> &&callback,
|
||||
std::function<void(const DrogonDbException &)> &&errCallback) {
|
||||
Mapper<T>::deleteByPrimaryKey(key,
|
||||
std::move(callback),
|
||||
std::move(errCallback));
|
||||
};
|
||||
co_return co_await internal::MapperAwaiter<size_t>(std::move(lb));
|
||||
}
|
||||
};
|
||||
} // namespace orm
|
||||
} // namespace drogon
|
||||
#endif
|
|
@ -46,7 +46,7 @@ namespace internal
|
|||
#ifdef __cpp_impl_coroutine
|
||||
struct SqlAwaiter : public CallbackAwaiter<Result>
|
||||
{
|
||||
SqlAwaiter(internal::SqlBinder &&binder) : binder_(binder)
|
||||
SqlAwaiter(internal::SqlBinder &&binder) : binder_(std::move(binder))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <drogon/utils/string_view.h>
|
||||
#include <drogon/utils/optional.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
@ -262,7 +263,7 @@ class CallbackHolder : public CallbackHolderBase
|
|||
return field.as<ValueType>();
|
||||
}
|
||||
};
|
||||
class SqlBinder
|
||||
class SqlBinder : public trantor::NonCopyable
|
||||
{
|
||||
using self = SqlBinder;
|
||||
|
||||
|
@ -295,6 +296,29 @@ class SqlBinder
|
|||
type_(type)
|
||||
{
|
||||
}
|
||||
SqlBinder(SqlBinder &&that)
|
||||
: sqlPtr_(std::move(that.sqlPtr_)),
|
||||
sqlViewPtr_(that.sqlViewPtr_),
|
||||
sqlViewLength_(that.sqlViewLength_),
|
||||
client_(that.client_),
|
||||
parametersNumber_(that.parametersNumber_),
|
||||
parameters_(std::move(that.parameters_)),
|
||||
lengths_(std::move(that.lengths_)),
|
||||
formats_(std::move(that.formats_)),
|
||||
objs_(std::move(that.objs_)),
|
||||
mode_(that.mode_),
|
||||
callbackHolder_(std::move(that.callbackHolder_)),
|
||||
exceptionCallback_(std::move(that.exceptionCallback_)),
|
||||
exceptionPtrCallback_(std::move(that.exceptionPtrCallback_)),
|
||||
execed_(that.execed_),
|
||||
destructed_(that.destructed_),
|
||||
isExceptionPtr_(that.isExceptionPtr_),
|
||||
type_(that.type_)
|
||||
{
|
||||
// set the execed_ to true to avoid the same sql being executed twice.
|
||||
that.execed_ = true;
|
||||
}
|
||||
SqlBinder &operator=(SqlBinder &&that) = delete;
|
||||
~SqlBinder();
|
||||
template <typename CallbackType,
|
||||
typename traits = FunctionTraits<CallbackType>>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <drogon/config.h>
|
||||
#include <drogon/orm/DbClient.h>
|
||||
#include <drogon/orm/DbTypes.h>
|
||||
#include <drogon/orm/CoroMapper.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
@ -33,9 +34,9 @@ using namespace drogon::orm;
|
|||
#define GREEN "\033[32m" /* Green */
|
||||
|
||||
#ifdef __cpp_impl_coroutine
|
||||
constexpr int postgre_tests = 47;
|
||||
constexpr int postgre_tests = 50;
|
||||
constexpr int mysql_tests = 47;
|
||||
constexpr int sqlite_tests = 49;
|
||||
constexpr int sqlite_tests = 51;
|
||||
#else
|
||||
constexpr int postgre_tests = 44;
|
||||
constexpr int mysql_tests = 45;
|
||||
|
@ -761,20 +762,61 @@ void doPostgreTest(const drogon::orm::DbClientPtr &clientPtr)
|
|||
std::cerr << e.base().what() << std::endl;
|
||||
testOutput(false, "postgresql - DbClient coroutine interface(1)");
|
||||
}
|
||||
/// 7.3 Transactions
|
||||
/// 7.3 CoroMapper
|
||||
try
|
||||
{
|
||||
CoroMapper<Users> mapper(clientPtr);
|
||||
auto user = co_await mapper.findByPrimaryKey(2);
|
||||
testOutput(true, "postgresql - ORM mapper coroutine interface(0)");
|
||||
}
|
||||
catch (const DrogonDbException &e)
|
||||
{
|
||||
std::cerr << "error";
|
||||
std::cerr << e.base().what() << std::endl;
|
||||
testOutput(false,
|
||||
"postgresql - ORM mapper coroutine interface(0)");
|
||||
}
|
||||
try
|
||||
{
|
||||
CoroMapper<Users> mapper(clientPtr);
|
||||
auto user = co_await mapper.findByPrimaryKey(314);
|
||||
testOutput(false, "postgresql - ORM mapper coroutine interface(1)");
|
||||
}
|
||||
catch (const DrogonDbException &e)
|
||||
{
|
||||
std::cerr << e.base().what() << std::endl;
|
||||
testOutput(true, "postgresql - ORM mapper coroutine interface(1)");
|
||||
}
|
||||
try
|
||||
{
|
||||
CoroMapper<Users> mapper(clientPtr);
|
||||
auto users = co_await mapper.findAll();
|
||||
auto count = co_await mapper.count();
|
||||
testOutput(users.size() == count,
|
||||
"postgresql - ORM mapper coroutine interface(2)");
|
||||
}
|
||||
catch (const DrogonDbException &e)
|
||||
{
|
||||
std::cerr << e.base().what() << std::endl;
|
||||
testOutput(true, "postgresql - ORM mapper coroutine interface(2)");
|
||||
}
|
||||
/// 7.4 Transactions
|
||||
try
|
||||
{
|
||||
auto trans = co_await clientPtr->newTransactionCoro();
|
||||
auto result =
|
||||
co_await trans->execSqlCoro("select * from users where 1=$1;",
|
||||
1);
|
||||
testOutput(result.size() != 0,
|
||||
"postgresql - DbClient coroutine interface(2)");
|
||||
testOutput(
|
||||
result.size() != 0,
|
||||
"postgresql - DbClient coroutine transaction interface(0)");
|
||||
}
|
||||
catch (const DrogonDbException &e)
|
||||
{
|
||||
std::cerr << e.base().what() << std::endl;
|
||||
testOutput(false, "postgresql - DbClient coroutine interface(2)");
|
||||
testOutput(
|
||||
false,
|
||||
"postgresql - DbClient coroutine transaction interface(0)");
|
||||
}
|
||||
};
|
||||
drogon::sync_wait(coro_test());
|
||||
|
@ -2109,6 +2151,34 @@ void doSqliteTest(const drogon::orm::DbClientPtr &clientPtr)
|
|||
std::cerr << e.base().what() << std::endl;
|
||||
testOutput(false, "sqlite3 - DbClient coroutine interface(1)");
|
||||
}
|
||||
/// 7.3 ORM CoroMapper
|
||||
try
|
||||
{
|
||||
auto mapper = CoroMapper<Users>(clientPtr);
|
||||
auto user = co_await mapper.findOne(
|
||||
Criteria(Users::Cols::_id, CompareOperator::EQ, 1));
|
||||
testOutput(true, "sqlite3 - CoroMapper coroutine interface(0)");
|
||||
}
|
||||
catch (const DrogonDbException &e)
|
||||
{
|
||||
std::cerr << e.base().what() << std::endl;
|
||||
testOutput(false, "sqlite3 - CoroMapper coroutine interface(0)");
|
||||
}
|
||||
try
|
||||
{
|
||||
auto mapper = CoroMapper<Users>(clientPtr);
|
||||
auto users = co_await mapper.findBy(
|
||||
Criteria(Users::Cols::_id, CompareOperator::EQ, 1));
|
||||
testOutput(users.size() == 1,
|
||||
"sqlite3 - CoroMapper coroutine interface(1)");
|
||||
}
|
||||
catch (const DrogonDbException &e)
|
||||
{
|
||||
std::cerr << e.base().what() << std::endl;
|
||||
testOutput(false, "sqlite3 - CoroMapper coroutine interface(1)");
|
||||
}
|
||||
co_await drogon::sleepCoro(
|
||||
trantor::EventLoop::getEventLoopOfCurrentThread(), 1.0s);
|
||||
};
|
||||
drogon::sync_wait(coro_test());
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue