Modify Mapper and CoroMapper templates (#722)

This commit is contained in:
An Tao 2021-02-20 10:00:28 +08:00 committed by GitHub
parent fd720d55d9
commit ffda84627b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 333 additions and 98 deletions

View File

@ -27,7 +27,7 @@ struct MapperAwaiter : public CallbackAwaiter<ReturnType>
{
using MapperFunction =
std::function<void(std::function<void(ReturnType result)> &&,
std::function<void(const DrogonDbException &)> &&)>;
std::function<void(const std::exception_ptr &)> &&)>;
MapperAwaiter(MapperFunction &&function) : function_(std::move(function))
{
}
@ -38,8 +38,8 @@ struct MapperAwaiter : public CallbackAwaiter<ReturnType>
this->setValue(std::move(result));
handle.resume();
},
[handle, this](const DrogonDbException &e) {
this->setException(std::make_exception_ptr(e));
[handle, this](const std::exception_ptr &e) {
this->setException(e);
handle.resume();
});
}
@ -66,36 +66,86 @@ class CoroMapper : public Mapper<T>
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));
if constexpr (!std::is_same<typename T::PrimaryKeyType, void>::value)
{
auto lb = [this,
key](std::function<void(T)> &&callback,
std::function<void(const std::exception_ptr &)>
&&errCallback) mutable {
static_assert(
!std::is_same<typename T::PrimaryKeyType, void>::value,
"No primary key in the table!");
static_assert(
internal::has_sqlForFindingByPrimaryKey<T>::value,
"No function member named sqlForFindingByPrimaryKey, "
"please "
"make sure that the model class is generated by the latest "
"version of drogon_ctl");
// return findFutureOne(Criteria(T::primaryKeyName, key));
std::string sql = T::sqlForFindingByPrimaryKey();
if (this->forUpdate_)
{
sql += " for update";
}
this->clear();
auto binder = *(this->client_) << std::move(sql);
this->outputPrimeryKeyToBinder(key, binder);
binder >> [callback = std::move(callback),
errCallback](const Result &r) {
if (r.size() == 0)
{
errCallback(std::make_exception_ptr(
UnexpectedRows("0 rows found")));
}
else if (r.size() > 1)
{
errCallback(std::make_exception_ptr(
UnexpectedRows("Found more than one row")));
}
else
{
callback(T(r[0]));
}
};
binder >> std::move(errCallback);
binder.exec();
};
co_return co_await internal::MapperAwaiter<T>(std::move(lb));
co_return co_await internal::MapperAwaiter<T>(std::move(lb));
}
else
{
LOG_FATAL << "The table must have a primary key";
abort();
}
}
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));
co_return co_await findBy(Criteria());
}
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));
std::function<void(const std::exception_ptr &)> &&errCallback) {
std::string sql = "select count(*) from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = this->replaceSqlPlaceHolder(sql, "$?");
}
this->clear();
auto binder = *(this->client_) << std::move(sql);
if (criteria)
criteria.outputArgs(binder);
binder >> [callback = std::move(callback)](const Result &r) {
assert(r.size() == 1);
callback(r[0][(Row::SizeType)0].as<size_t>());
};
binder >> std::move(errCallback);
};
co_return co_await internal::MapperAwaiter<size_t>(std::move(lb));
}
@ -104,10 +154,59 @@ class CoroMapper : public Mapper<T>
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));
std::function<void(const std::exception_ptr &)> &&errCallback) {
std::string sql = "select * from ";
sql += T::tableName;
bool hasParameters = false;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
hasParameters = true;
}
sql.append(this->orderByString_);
if (this->limit_ > 0)
{
hasParameters = true;
sql.append(" limit $?");
}
if (this->offset_ > 0)
{
hasParameters = true;
sql.append(" offset $?");
}
if (hasParameters)
sql = this->replaceSqlPlaceHolder(sql, "$?");
if (this->forUpdate_)
{
sql += " for update";
}
auto binder = *(this->client_) << std::move(sql);
if (criteria)
criteria.outputArgs(binder);
if (this->limit_ > 0)
binder << this->limit_;
if (this->offset_)
binder << this->offset_;
this->clear();
binder >> [errCallback,
callback = std::move(callback)](const Result &r) {
if (r.size() == 0)
{
errCallback(std::make_exception_ptr(
UnexpectedRows("0 rows found")));
}
else if (r.size() > 1)
{
errCallback(std::make_exception_ptr(
UnexpectedRows("Found more than one row")));
}
else
{
callback(T(r[0]));
}
};
binder >> std::move(errCallback);
};
co_return co_await internal::MapperAwaiter<T>(std::move(lb));
}
@ -116,30 +215,141 @@ class CoroMapper : public Mapper<T>
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));
std::function<void(const std::exception_ptr &)> &&errCallback) {
std::string sql = "select * from ";
sql += T::tableName;
bool hasParameters = false;
if (criteria)
{
hasParameters = true;
sql += " where ";
sql += criteria.criteriaString();
}
sql.append(this->orderByString_);
if (this->limit_ > 0)
{
hasParameters = true;
sql.append(" limit $?");
}
if (this->offset_ > 0)
{
hasParameters = true;
sql.append(" offset $?");
}
if (hasParameters)
sql = this->replaceSqlPlaceHolder(sql, "$?");
if (this->forUpdate_)
{
sql += " for update";
}
auto binder = *(this->client_) << std::move(sql);
if (criteria)
criteria.outputArgs(binder);
if (this->limit_ > 0)
binder << this->limit_;
if (this->offset_)
binder << this->offset_;
this->clear();
binder >> [callback = std::move(callback)](const Result &r) {
std::vector<T> ret;
for (auto const &row : r)
{
ret.push_back(T(row));
}
callback(ret);
};
binder >> 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));
};
auto lb =
[this, obj](
std::function<void(T)> &&callback,
std::function<void(const std::exception_ptr &)> &&errCallback) {
this->clear();
bool needSelection = false;
auto binder = *(this->client_)
<< obj.sqlForInserting(needSelection);
obj.outputArgs(binder);
auto client = this->client_;
binder >> [client,
callback = std::move(callback),
obj,
needSelection,
errCallback](const Result &r) {
assert(r.affectedRows() == 1);
if (client->type() == ClientType::PostgreSQL)
{
if (needSelection)
{
assert(r.size() == 1);
callback(T(r[0]));
}
else
{
callback(obj);
}
}
else // Mysql or Sqlite3
{
auto id = r.insertId();
auto newObj = obj;
newObj.updateId(id);
if (needSelection)
{
auto tmp = Mapper<T>(client);
tmp.findByPrimaryKey(
newObj.getPrimaryKey(),
callback,
[errCallback](const DrogonDbException &err) {
errCallback(std::make_exception_ptr(
Failure(err.base().what())));
});
}
else
{
callback(newObj);
}
}
};
binder >> 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));
};
auto lb =
[this, obj](
std::function<void(const size_t)> &&callback,
std::function<void(const std::exception_ptr &)> &&errCallback) {
this->clear();
static_assert(
!std::is_same<typename T::PrimaryKeyType, void>::value,
"No primary key in the table!");
std::string sql = "update ";
sql += T::tableName;
sql += " set ";
for (auto const &colName : obj.updateColumns())
{
sql += colName;
sql += " = $?,";
}
sql[sql.length() - 1] = ' '; // Replace the last ','
this->makePrimaryKeyCriteria(sql);
sql = this->replaceSqlPlaceHolder(sql, "$?");
auto binder = *(this->client_) << std::move(sql);
obj.updateArgs(binder);
this->outputPrimeryKeyToBinder(obj.getPrimaryKey(), binder);
binder >> [callback = std::move(callback)](const Result &r) {
callback(r.affectedRows());
};
binder >> std::move(errCallback);
};
co_return co_await internal::MapperAwaiter<size_t>(std::move(lb));
}
inline const Task<size_t> deleteOne(const T &obj)
@ -147,10 +357,24 @@ class CoroMapper : public Mapper<T>
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));
std::function<void(const std::exception_ptr &)> &&errCallback) {
this->clear();
static_assert(
!std::is_same<typename T::PrimaryKeyType, void>::value,
"No primary key in the table!");
std::string sql = "delete from ";
sql += T::tableName;
sql += " ";
this->makePrimaryKeyCriteria(sql);
sql = this->replaceSqlPlaceHolder(sql, "$?");
auto binder = *(this->client_) << std::move(sql);
this->outputPrimeryKeyToBinder(obj.getPrimaryKey(), binder);
binder >> [callback = std::move(callback)](const Result &r) {
callback(r.affectedRows());
};
binder >> std::move(errCallback);
};
co_return co_await internal::MapperAwaiter<size_t>(std::move(lb));
}
@ -159,23 +383,53 @@ class CoroMapper : public Mapper<T>
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));
std::function<void(const std::exception_ptr &)> &&errCallback) {
this->clear();
static_assert(
!std::is_same<typename T::PrimaryKeyType, void>::value,
"No primary key in the table!");
std::string sql = "delete from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = this->replaceSqlPlaceHolder(sql, "$?");
}
auto binder = *(this->client_) << std::move(sql);
if (criteria)
{
criteria.outputArgs(binder);
}
binder >> [callback = std::move(callback)](const Result &r) {
callback(r.affectedRows());
};
binder >> 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));
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value,
"No primary key in the table!");
static_assert(
internal::has_sqlForDeletingByPrimaryKey<T>::value,
"No function member named sqlForDeletingByPrimaryKey, please "
"make sure that the model class is generated by the latest "
"version of drogon_ctl");
auto lb = [this, key](std::function<void(const size_t)> &&callback,
std::function<void(const std::exception_ptr &)>
&&errCallback) {
this->clear();
auto binder = *(this->client_) << T::sqlForDeletingByPrimaryKey();
this->outputPrimeryKeyToBinder(key, binder);
binder >> [callback = std::move(callback)](const Result &r) {
callback(r.affectedRows());
};
binder >> std::move(errCallback);
};
co_return co_await internal::MapperAwaiter<size_t>(std::move(lb));
}
};

View File

@ -326,25 +326,13 @@ class Mapper
binder >> [prom](const Result &r) {
if (r.size() == 0)
{
try
{
throw UnexpectedRows("0 rows found");
}
catch (...)
{
prom->set_exception(std::current_exception());
}
prom->set_exception(
std::make_exception_ptr(UnexpectedRows("0 rows found")));
}
else if (r.size() > 1)
{
try
{
throw UnexpectedRows("Found more than one row");
}
catch (...)
{
prom->set_exception(std::current_exception());
}
prom->set_exception(std::make_exception_ptr(
UnexpectedRows("Found more than one row")));
}
else
{
@ -633,7 +621,7 @@ class Mapper
std::future<size_t> deleteFutureByPrimaryKey(
const TraitsPKType &key) noexcept;
private:
protected:
DbClientPtr client_;
size_t limit_{0};
size_t offset_{0};
@ -863,25 +851,13 @@ inline std::future<T> Mapper<T>::findFutureOne(
binder >> [prom](const Result &r) {
if (r.size() == 0)
{
try
{
throw UnexpectedRows("0 rows found");
}
catch (...)
{
prom->set_exception(std::current_exception());
}
prom->set_exception(
std::make_exception_ptr(UnexpectedRows("0 rows found")));
}
else if (r.size() > 1)
{
try
{
throw UnexpectedRows("Found more than one row");
}
catch (...)
{
prom->set_exception(std::current_exception());
}
prom->set_exception(std::make_exception_ptr(
UnexpectedRows("Found more than one row")));
}
else
{
@ -1244,14 +1220,8 @@ inline std::future<T> Mapper<T>::insertFuture(const T &obj) noexcept
newObj.getPrimaryKey(),
[prom](T selObj) { prom->set_value(selObj); },
[prom](const DrogonDbException &e) {
try
{
throw e;
}
catch (...)
{
prom->set_exception(std::current_exception());
}
prom->set_exception(
std::make_exception_ptr(Failure(e.base().what())));
});
}
else

View File

@ -36,7 +36,7 @@ using namespace drogon::orm;
#ifdef __cpp_impl_coroutine
constexpr int postgre_tests = 50;
constexpr int mysql_tests = 47;
constexpr int sqlite_tests = 51;
constexpr int sqlite_tests = 52;
#else
constexpr int postgre_tests = 44;
constexpr int mysql_tests = 45;
@ -2177,6 +2177,17 @@ void doSqliteTest(const drogon::orm::DbClientPtr &clientPtr)
std::cerr << e.base().what() << std::endl;
testOutput(false, "sqlite3 - CoroMapper coroutine interface(1)");
}
try
{
auto mapper = CoroMapper<Users>(clientPtr);
auto n = co_await mapper.deleteByPrimaryKey(1);
testOutput(n == 1, "sqlite3 - CoroMapper coroutine interface(2)");
}
catch (const DrogonDbException &e)
{
std::cerr << e.base().what() << std::endl;
testOutput(false, "sqlite3 - CoroMapper coroutine interface(2)");
}
co_await drogon::sleepCoro(
trantor::EventLoop::getEventLoopOfCurrentThread(), 1.0s);
};