1843 lines
54 KiB
C
1843 lines
54 KiB
C
|
/**
|
||
|
*
|
||
|
* @file Mapper.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
|
||
|
#include <drogon/orm/Criteria.h>
|
||
|
#include <drogon/orm/DbClient.h>
|
||
|
#include <drogon/utils/Utilities.h>
|
||
|
#include <string>
|
||
|
#include <type_traits>
|
||
|
#include <vector>
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
using ssize_t = std::intptr_t;
|
||
|
#endif
|
||
|
|
||
|
namespace drogon
|
||
|
{
|
||
|
namespace orm
|
||
|
{
|
||
|
enum class SortOrder
|
||
|
{
|
||
|
ASC,
|
||
|
DESC
|
||
|
};
|
||
|
namespace internal
|
||
|
{
|
||
|
template <typename T, bool hasPrimaryKey = true>
|
||
|
struct Traits
|
||
|
{
|
||
|
using type = typename T::PrimaryKeyType;
|
||
|
};
|
||
|
template <typename T>
|
||
|
struct Traits<T, false>
|
||
|
{
|
||
|
using type = int;
|
||
|
};
|
||
|
template <typename T>
|
||
|
struct has_sqlForFindingByPrimaryKey
|
||
|
{
|
||
|
private:
|
||
|
using yes = std::true_type;
|
||
|
using no = std::false_type;
|
||
|
|
||
|
template <typename U>
|
||
|
static auto test(int) -> decltype(U::sqlForFindingByPrimaryKey(), yes());
|
||
|
|
||
|
template <typename>
|
||
|
static no test(...);
|
||
|
|
||
|
public:
|
||
|
static constexpr bool value =
|
||
|
std::is_same<decltype(test<T>(0)), yes>::value;
|
||
|
};
|
||
|
template <typename T>
|
||
|
struct has_sqlForDeletingByPrimaryKey
|
||
|
{
|
||
|
private:
|
||
|
using yes = std::true_type;
|
||
|
using no = std::false_type;
|
||
|
|
||
|
template <typename U>
|
||
|
static auto test(int)
|
||
|
-> decltype(U::sqlForDeletingByPrimaryKey().length(), yes());
|
||
|
|
||
|
template <typename>
|
||
|
static no test(...);
|
||
|
|
||
|
public:
|
||
|
static constexpr bool value =
|
||
|
std::is_same<decltype(test<T>(0)), yes>::value;
|
||
|
};
|
||
|
} // namespace internal
|
||
|
|
||
|
/**
|
||
|
* @brief The mapper template
|
||
|
*
|
||
|
* @tparam T The type of the model to be mapped.
|
||
|
*
|
||
|
* @details The mapping between the model object and the database table is
|
||
|
* performed by the Mapper class template. The Mapper class template
|
||
|
* encapsulates common operations such as adding, deleting, and changing, so
|
||
|
* that the user can perform the above operations without writing a SQL
|
||
|
* statement.
|
||
|
*
|
||
|
* The construction of the Mapper object is very simple. The template
|
||
|
* parameter is the type of the model you want to access. The constructor has
|
||
|
* only one parameter, which is the DbClient smart pointer mentioned earlier. As
|
||
|
* mentioned earlier, the Transaction class is a subclass of DbClient, so you
|
||
|
* can also construct a Mapper object with a smart pointer to a transaction,
|
||
|
* which means that the Mapper mapping also supports transactions.
|
||
|
*
|
||
|
* Like DbClient, Mapper also provides asynchronous and synchronous interfaces.
|
||
|
* The synchronous interface is blocked and may throw an exception. The returned
|
||
|
* future object is blocked in the get() method and may throw an exception. The
|
||
|
* normal asynchronous interface does not throw an exception, but returns the
|
||
|
* result through two callbacks (result callback and exception callback). The
|
||
|
* type of the exception callback is the same as that in the DbClient interface.
|
||
|
* The result callback is also divided into several categories according to the
|
||
|
* interface function.
|
||
|
*/
|
||
|
template <typename T>
|
||
|
class Mapper
|
||
|
{
|
||
|
public:
|
||
|
/**
|
||
|
* @brief Construct a new Mapper object
|
||
|
*
|
||
|
* @param client The smart pointer to the database client object.
|
||
|
*/
|
||
|
explicit Mapper(DbClientPtr client) : client_(std::move(client))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Add a limit to the query.
|
||
|
*
|
||
|
* @param limit The limit
|
||
|
* @return Mapper<T>& The Mapper itself.
|
||
|
*/
|
||
|
Mapper<T> &limit(size_t limit);
|
||
|
|
||
|
/**
|
||
|
* @brief Add a offset to the query.
|
||
|
*
|
||
|
* @param offset The offset.
|
||
|
* @return Mapper<T>& The Mapper itself.
|
||
|
*/
|
||
|
Mapper<T> &offset(size_t offset);
|
||
|
|
||
|
/**
|
||
|
* @brief Set the order of the results.
|
||
|
*
|
||
|
* @param colName the column name, the results are sorted by that column
|
||
|
* @param order Ascending or descending order
|
||
|
* @return Mapper<T>& The Mapper itself.
|
||
|
*/
|
||
|
Mapper<T> &orderBy(const std::string &colName,
|
||
|
const SortOrder &order = SortOrder::ASC);
|
||
|
|
||
|
/**
|
||
|
* @brief Set the order of the results.
|
||
|
*
|
||
|
* @param colIndex the column index, the results are sorted by that column
|
||
|
* @param order Ascending or descending order
|
||
|
* @return Mapper<T>& The Mapper itself.
|
||
|
*/
|
||
|
Mapper<T> &orderBy(size_t colIndex,
|
||
|
const SortOrder &order = SortOrder::ASC);
|
||
|
|
||
|
/**
|
||
|
* @brief Set limit and offset to achieve pagination.
|
||
|
* This method will override limit() and offset(), and will be overriden by
|
||
|
* them.
|
||
|
*
|
||
|
* @param page The page number
|
||
|
* @param perPage The number of columns per page
|
||
|
* @return Mapper<T>& The Mapper itself.
|
||
|
*/
|
||
|
Mapper<T> &paginate(size_t page, size_t perPage);
|
||
|
|
||
|
/**
|
||
|
* @brief Lock the result for updating.
|
||
|
*
|
||
|
* @return Mapper<T>& The Mapper itself.
|
||
|
*/
|
||
|
Mapper<T> &forUpdate();
|
||
|
|
||
|
using SingleRowCallback = std::function<void(T)>;
|
||
|
using MultipleRowsCallback = std::function<void(std::vector<T>)>;
|
||
|
using CountCallback = std::function<void(const size_t)>;
|
||
|
|
||
|
using TraitsPKType = typename internal::
|
||
|
Traits<T, !std::is_same<typename T::PrimaryKeyType, void>::value>::type;
|
||
|
|
||
|
/**
|
||
|
* @brief Find a record by the primary key.
|
||
|
*
|
||
|
* @param key The value of the primary key.
|
||
|
* @return T The record of the primary key.
|
||
|
* @note If no hit record exists, an UnexpectedRows exception is thrown.
|
||
|
*/
|
||
|
|
||
|
template <typename U = T>
|
||
|
inline typename std::enable_if<
|
||
|
!std::is_same<typename U::PrimaryKeyType, void>::value,
|
||
|
T>::type
|
||
|
findByPrimaryKey(const TraitsPKType &key) noexcept(false)
|
||
|
{
|
||
|
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 findOne(Criteria(T::primaryKeyName, key));
|
||
|
std::string sql = T::sqlForFindingByPrimaryKey();
|
||
|
if (forUpdate_)
|
||
|
{
|
||
|
sql += " for update";
|
||
|
}
|
||
|
clear();
|
||
|
Result r(nullptr);
|
||
|
{
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
outputPrimeryKeyToBinder(key, binder);
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // exec may be throw exception;
|
||
|
}
|
||
|
if (r.size() == 0)
|
||
|
{
|
||
|
throw UnexpectedRows("0 rows found");
|
||
|
}
|
||
|
else if (r.size() > 1)
|
||
|
{
|
||
|
throw UnexpectedRows("Found more than one row");
|
||
|
}
|
||
|
auto row = r[0];
|
||
|
return T(row);
|
||
|
}
|
||
|
|
||
|
template <typename U = T>
|
||
|
inline typename std::enable_if<
|
||
|
std::is_same<typename U::PrimaryKeyType, void>::value,
|
||
|
T>::type
|
||
|
findByPrimaryKey(const TraitsPKType &key) noexcept(false)
|
||
|
{
|
||
|
LOG_FATAL << "The table must have a primary key";
|
||
|
abort();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously find a record by the primary key.
|
||
|
*
|
||
|
* @param key The value of the primary key.
|
||
|
* @param rcb Is called when a record is found.
|
||
|
* @param ecb Is called when an error occurs or a record cannot be found.
|
||
|
*/
|
||
|
template <typename U = T>
|
||
|
inline typename std::enable_if<
|
||
|
!std::is_same<typename U::PrimaryKeyType, void>::value,
|
||
|
void>::type
|
||
|
findByPrimaryKey(const TraitsPKType &key,
|
||
|
const SingleRowCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
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");
|
||
|
// findOne(Criteria(T::primaryKeyName, key), rcb, ecb);
|
||
|
std::string sql = T::sqlForFindingByPrimaryKey();
|
||
|
if (forUpdate_)
|
||
|
{
|
||
|
sql += " for update";
|
||
|
}
|
||
|
clear();
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
outputPrimeryKeyToBinder(key, binder);
|
||
|
binder >> [ecb, rcb](const Result &r) {
|
||
|
if (r.size() == 0)
|
||
|
{
|
||
|
ecb(UnexpectedRows("0 rows found"));
|
||
|
}
|
||
|
else if (r.size() > 1)
|
||
|
{
|
||
|
ecb(UnexpectedRows("Found more than one row"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rcb(T(r[0]));
|
||
|
}
|
||
|
};
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
template <typename U = T>
|
||
|
inline typename std::enable_if<
|
||
|
std::is_same<typename U::PrimaryKeyType, void>::value,
|
||
|
void>::type
|
||
|
findByPrimaryKey(const TraitsPKType &key,
|
||
|
const SingleRowCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
LOG_FATAL << "The table must have a primary key";
|
||
|
abort();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously find a record by the primary key.
|
||
|
*
|
||
|
* @param key The value of the primary key.
|
||
|
* @return std::future<T> The future object with which user can get the
|
||
|
* result.
|
||
|
* @note If no hit record exists, an UnexpectedRows exception is thrown when
|
||
|
* user calls the get() method of the future object.
|
||
|
*/
|
||
|
template <typename U = T>
|
||
|
inline typename std::enable_if<
|
||
|
!std::is_same<typename U::PrimaryKeyType, void>::value,
|
||
|
std::future<T>>::type
|
||
|
findFutureByPrimaryKey(const TraitsPKType &key) noexcept
|
||
|
{
|
||
|
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 (forUpdate_)
|
||
|
{
|
||
|
sql += " for update";
|
||
|
}
|
||
|
clear();
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
outputPrimeryKeyToBinder(key, binder);
|
||
|
|
||
|
std::shared_ptr<std::promise<T>> prom =
|
||
|
std::make_shared<std::promise<T>>();
|
||
|
binder >> [prom](const Result &r) {
|
||
|
if (r.size() == 0)
|
||
|
{
|
||
|
prom->set_exception(
|
||
|
std::make_exception_ptr(UnexpectedRows("0 rows found")));
|
||
|
}
|
||
|
else if (r.size() > 1)
|
||
|
{
|
||
|
prom->set_exception(std::make_exception_ptr(
|
||
|
UnexpectedRows("Found more than one row")));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
prom->set_value(T(r[0]));
|
||
|
}
|
||
|
};
|
||
|
binder >>
|
||
|
[prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
|
||
|
template <typename U = T>
|
||
|
inline typename std::enable_if<
|
||
|
std::is_same<typename U::PrimaryKeyType, void>::value,
|
||
|
std::future<U>>::type
|
||
|
findFutureByPrimaryKey(const TraitsPKType &key) noexcept
|
||
|
{
|
||
|
LOG_FATAL << "The table must have a primary key";
|
||
|
abort();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Find all the records in the table.
|
||
|
*
|
||
|
* @return std::vector<T> The vector of all the records.
|
||
|
*/
|
||
|
std::vector<T> findAll() noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously find all the records in the table.
|
||
|
*
|
||
|
* @param rcb is called with the result.
|
||
|
* @param ecb is called when an error occurs.
|
||
|
*/
|
||
|
void findAll(const MultipleRowsCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously find all the records in the table.
|
||
|
*
|
||
|
* @return std::future<std::vector<T>> The future object with which user can
|
||
|
* get the result.
|
||
|
*/
|
||
|
std::future<std::vector<T>> findFutureAll() noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Get the count of rows that match the given criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @return size_t The number of rows.
|
||
|
*/
|
||
|
size_t count(const Criteria &criteria = Criteria()) noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously get the number of rows that match the given
|
||
|
* criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @param rcb is clalled with the result.
|
||
|
* @param ecb is called when an error occurs.
|
||
|
*/
|
||
|
void count(const Criteria &criteria,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously get the number of rows that match the given
|
||
|
* criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @return std::future<size_t> The future object with which user can get the
|
||
|
* number of rows
|
||
|
*/
|
||
|
std::future<size_t> countFuture(
|
||
|
const Criteria &criteria = Criteria()) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Find one record that matches the given criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @return T The result record.
|
||
|
* @note if the number of rows is greater than one or equal to zero, an
|
||
|
* UnexpectedRows exception is thrown.
|
||
|
*/
|
||
|
T findOne(const Criteria &criteria) noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously find one record that matches the given criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @param rcb is called with the result.
|
||
|
* @param ecb is called when an error occurs.
|
||
|
*/
|
||
|
void findOne(const Criteria &criteria,
|
||
|
const SingleRowCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously find one record that matches the given criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @return std::future<T> The future object with which user can get the
|
||
|
* result.
|
||
|
* @note if the number of rows is greater than one or equal to zero, an
|
||
|
* UnexpectedRows exception is thrown when the get() method of the future
|
||
|
* object is called.
|
||
|
*/
|
||
|
std::future<T> findFutureOne(const Criteria &criteria) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Select the rows that match the given criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @return std::vector<T> The vector of rows that match the given criteria.
|
||
|
*/
|
||
|
std::vector<T> findBy(const Criteria &criteria) noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously select the rows that match the given criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @param rcb is called with the result.
|
||
|
* @param ecb is called when an error occurs.
|
||
|
*/
|
||
|
void findBy(const Criteria &criteria,
|
||
|
const MultipleRowsCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously select the rows that match the given criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @return std::future<std::vector<T>> The future object with which user can
|
||
|
* get the result.
|
||
|
*/
|
||
|
std::future<std::vector<T>> findFutureBy(const Criteria &criteria) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Insert a row into the table.
|
||
|
*
|
||
|
* @param obj The object to be inserted.
|
||
|
* @note The auto-increased primary key (if it exists) is set to the obj
|
||
|
* argument after the method returns.
|
||
|
*/
|
||
|
void insert(T &obj) noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously insert a row into the table.
|
||
|
*
|
||
|
* @param obj The object to be inserted.
|
||
|
* @param rcb is called with the result (with the auto-increased primary key
|
||
|
* (if it exists)).
|
||
|
* @param ecb is called when an error occurs.
|
||
|
*/
|
||
|
void insert(const T &obj,
|
||
|
const SingleRowCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously insert a row into the table.
|
||
|
*
|
||
|
* @return std::future<T> The future object with which user can get the
|
||
|
* result (with the auto-increased primary key (if it exists)).
|
||
|
*/
|
||
|
std::future<T> insertFuture(const T &) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Update a record.
|
||
|
*
|
||
|
* @param obj The record.
|
||
|
* @return size_t The number of updated records. It only could be 0 or 1.
|
||
|
* @note The table must have a primary key.
|
||
|
*/
|
||
|
size_t update(const T &obj) noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously update a record.
|
||
|
*
|
||
|
* @param obj The record.
|
||
|
* @param rcb is called with the number of updated records.
|
||
|
* @param ecb is called when an error occurs.
|
||
|
* @note The table must have a primary key.
|
||
|
*/
|
||
|
void update(const T &obj,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously update a record.
|
||
|
*
|
||
|
* @param obj The record.
|
||
|
* @return std::future<size_t> The future object with which user can get the
|
||
|
* number of updated records.
|
||
|
* @note The table must have a primary key.
|
||
|
*/
|
||
|
std::future<size_t> updateFuture(const T &obj) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Update a record that match both the primary key and the given
|
||
|
* criteria.
|
||
|
*
|
||
|
* @param colNames Columns to update.
|
||
|
* @param criteria The criteria.
|
||
|
* @param args New value of target columns.
|
||
|
* @return size_t The number of updated records. It only could be 0 or 1.
|
||
|
* @note The table must have a primary key.
|
||
|
*/
|
||
|
template <typename... Arguments>
|
||
|
size_t updateBy(const std::vector<std::string> &colNames,
|
||
|
const Criteria &criteria,
|
||
|
Arguments &&...args) noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously select the rows that match both the primary key and
|
||
|
* the given criteria.
|
||
|
*
|
||
|
* @param colNames Columns to update.
|
||
|
* @param criteria The criteria.
|
||
|
* @param rcb is called with the result.
|
||
|
* @param ecb is called when an error occurs.
|
||
|
* @param args New value of target columns.
|
||
|
*/
|
||
|
template <typename... Arguments>
|
||
|
void updateBy(const std::vector<std::string> &colNames,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb,
|
||
|
const Criteria &criteria,
|
||
|
Arguments &&...args) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously update a record that match both the primary key and
|
||
|
* the given criteria.
|
||
|
*
|
||
|
* @param colNames Columns to update.
|
||
|
* @param criteria The criteria.
|
||
|
* @param args New value of target columns.
|
||
|
* @return std::future<size_t> The future object with which user can get the
|
||
|
* number of updated records.
|
||
|
* @note The table must have a primary key.
|
||
|
*/
|
||
|
template <typename... Arguments>
|
||
|
std::future<size_t> updateFutureBy(const std::vector<std::string> &colNames,
|
||
|
const Criteria &criteria,
|
||
|
Arguments &&...args) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Delete a record from the table.
|
||
|
*
|
||
|
* @param obj The record.
|
||
|
* @return size_t The number of deleted records.
|
||
|
* @note The table must have a primary key.
|
||
|
*/
|
||
|
size_t deleteOne(const T &obj) noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously delete a record from the table.
|
||
|
*
|
||
|
* @param obj The record.
|
||
|
* @param rcb is called with the number of deleted records.
|
||
|
* @param ecb is called when an error occurs.
|
||
|
* @note The table must have a primary key.
|
||
|
*/
|
||
|
void deleteOne(const T &obj,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously delete a record from the table.
|
||
|
*
|
||
|
* @param obj The record.
|
||
|
* @return std::future<size_t> The future object with which user can get the
|
||
|
* number of deleted records.
|
||
|
* @note The table must have a primary key.
|
||
|
*/
|
||
|
std::future<size_t> deleteFutureOne(const T &obj) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Delete records that satisfy the given criteria.
|
||
|
*
|
||
|
* @param criteria The criteria.
|
||
|
* @return size_t The number of deleted records.
|
||
|
*/
|
||
|
size_t deleteBy(const Criteria &criteria) noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Delete records that match the given criteria asynchronously.
|
||
|
*
|
||
|
* @param criteria The criteria
|
||
|
* @param rcb is called with the number of deleted records.
|
||
|
* @param ecb is called when an error occurs.
|
||
|
*/
|
||
|
void deleteBy(const Criteria &criteria,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Delete records that match the given criteria asynchronously.
|
||
|
*
|
||
|
* @param criteria The criteria
|
||
|
* @return std::future<size_t> The future object with which user can get the
|
||
|
* number of deleted records
|
||
|
*/
|
||
|
std::future<size_t> deleteFutureBy(const Criteria &criteria) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Delete the record that matches the given primary key.
|
||
|
*
|
||
|
* @param key The primary key.
|
||
|
* @return size_t The number of deleted records (1 or 0).
|
||
|
*/
|
||
|
size_t deleteByPrimaryKey(const TraitsPKType &key) noexcept(false);
|
||
|
|
||
|
/**
|
||
|
* @brief Asynchronously delete the record that matches the given primary
|
||
|
* key.
|
||
|
*
|
||
|
* @param key The primary key.
|
||
|
* @param rcb is called with the number of deleted records.
|
||
|
* @param ecb is called when an error occurs.
|
||
|
*/
|
||
|
void deleteByPrimaryKey(const TraitsPKType &key,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept;
|
||
|
|
||
|
/**
|
||
|
* @brief Delete the record that matches the given primary key
|
||
|
* asynchronously.
|
||
|
*
|
||
|
* @param key The primary key.
|
||
|
* @return std::future<size_t> The future object with which user can get the
|
||
|
* number of deleted records
|
||
|
*/
|
||
|
std::future<size_t> deleteFutureByPrimaryKey(
|
||
|
const TraitsPKType &key) noexcept;
|
||
|
|
||
|
protected:
|
||
|
DbClientPtr client_;
|
||
|
size_t limit_{0};
|
||
|
size_t offset_{0};
|
||
|
std::string orderByString_;
|
||
|
bool forUpdate_{false};
|
||
|
void clear()
|
||
|
{
|
||
|
limit_ = 0;
|
||
|
offset_ = 0;
|
||
|
orderByString_.clear();
|
||
|
forUpdate_ = false;
|
||
|
}
|
||
|
template <typename PKType = decltype(T::primaryKeyName)>
|
||
|
typename std::enable_if<std::is_same<const std::string, PKType>::value,
|
||
|
void>::type
|
||
|
makePrimaryKeyCriteria(std::string &sql)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += T::primaryKeyName;
|
||
|
sql += " = $?";
|
||
|
}
|
||
|
template <typename PKType = decltype(T::primaryKeyName)>
|
||
|
typename std::enable_if<
|
||
|
std::is_same<const std::vector<std::string>, PKType>::value,
|
||
|
void>::type
|
||
|
makePrimaryKeyCriteria(std::string &sql)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
for (size_t i = 0; i < T::primaryKeyName.size(); ++i)
|
||
|
{
|
||
|
sql += T::primaryKeyName[i];
|
||
|
sql += " = $?";
|
||
|
if (i < (T::primaryKeyName.size() - 1))
|
||
|
{
|
||
|
sql += " and ";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
template <typename PKType = decltype(T::primaryKeyName)>
|
||
|
typename std::enable_if<std::is_same<const std::string, PKType>::value,
|
||
|
void>::type
|
||
|
outputPrimeryKeyToBinder(const TraitsPKType &pk,
|
||
|
internal::SqlBinder &binder)
|
||
|
{
|
||
|
binder << pk;
|
||
|
}
|
||
|
template <typename PKType = decltype(T::primaryKeyName)>
|
||
|
typename std::enable_if<
|
||
|
std::is_same<const std::vector<std::string>, PKType>::value,
|
||
|
void>::type
|
||
|
outputPrimeryKeyToBinder(const TraitsPKType &pk,
|
||
|
internal::SqlBinder &binder)
|
||
|
{
|
||
|
tupleToBinder<typename T::PrimaryKeyType>(pk, binder);
|
||
|
}
|
||
|
|
||
|
template <typename TP, ssize_t N = std::tuple_size<TP>::value>
|
||
|
typename std::enable_if<(N > 1), void>::type tupleToBinder(
|
||
|
const TP &t,
|
||
|
internal::SqlBinder &binder)
|
||
|
{
|
||
|
tupleToBinder<TP, N - 1>(t, binder);
|
||
|
binder << std::get<N - 1>(t);
|
||
|
}
|
||
|
template <typename TP, ssize_t N = std::tuple_size<TP>::value>
|
||
|
typename std::enable_if<(N == 1), void>::type tupleToBinder(
|
||
|
const TP &t,
|
||
|
internal::SqlBinder &binder)
|
||
|
{
|
||
|
binder << std::get<0>(t);
|
||
|
}
|
||
|
|
||
|
std::string replaceSqlPlaceHolder(const std::string &sqlStr,
|
||
|
const std::string &holderStr) const;
|
||
|
};
|
||
|
|
||
|
template <typename T>
|
||
|
inline T Mapper<T>::findOne(const Criteria &criteria) noexcept(false)
|
||
|
{
|
||
|
std::string sql = "select * from ";
|
||
|
sql += T::tableName;
|
||
|
bool hasParameters = false;
|
||
|
if (criteria)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
hasParameters = true;
|
||
|
}
|
||
|
sql.append(orderByString_);
|
||
|
if (limit_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" limit $?");
|
||
|
}
|
||
|
if (offset_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" offset $?");
|
||
|
}
|
||
|
if (hasParameters)
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
if (forUpdate_)
|
||
|
{
|
||
|
sql += " for update";
|
||
|
}
|
||
|
Result r(nullptr);
|
||
|
{
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
if (limit_ > 0)
|
||
|
binder << limit_;
|
||
|
if (offset_)
|
||
|
binder << offset_;
|
||
|
clear();
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // exec may be throw exception;
|
||
|
}
|
||
|
if (r.size() == 0)
|
||
|
{
|
||
|
throw UnexpectedRows("0 rows found");
|
||
|
}
|
||
|
else if (r.size() > 1)
|
||
|
{
|
||
|
throw UnexpectedRows("Found more than one row");
|
||
|
}
|
||
|
auto row = r[0];
|
||
|
return T(row);
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::findOne(const Criteria &criteria,
|
||
|
const SingleRowCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
std::string sql = "select * from ";
|
||
|
sql += T::tableName;
|
||
|
bool hasParameters = false;
|
||
|
if (criteria)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
hasParameters = true;
|
||
|
}
|
||
|
sql.append(orderByString_);
|
||
|
if (limit_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" limit $?");
|
||
|
}
|
||
|
if (offset_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" offset $?");
|
||
|
}
|
||
|
if (hasParameters)
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
if (forUpdate_)
|
||
|
{
|
||
|
sql += " for update";
|
||
|
}
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
if (limit_ > 0)
|
||
|
binder << limit_;
|
||
|
if (offset_)
|
||
|
binder << offset_;
|
||
|
clear();
|
||
|
binder >> [ecb, rcb](const Result &r) {
|
||
|
if (r.size() == 0)
|
||
|
{
|
||
|
ecb(UnexpectedRows("0 rows found"));
|
||
|
}
|
||
|
else if (r.size() > 1)
|
||
|
{
|
||
|
ecb(UnexpectedRows("Found more than one row"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rcb(T(r[0]));
|
||
|
}
|
||
|
};
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline std::future<T> Mapper<T>::findFutureOne(
|
||
|
const Criteria &criteria) noexcept
|
||
|
{
|
||
|
std::string sql = "select * from ";
|
||
|
sql += T::tableName;
|
||
|
bool hasParameters = false;
|
||
|
if (criteria)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
hasParameters = true;
|
||
|
}
|
||
|
sql.append(orderByString_);
|
||
|
if (limit_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" limit $?");
|
||
|
}
|
||
|
if (offset_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" offset $?");
|
||
|
}
|
||
|
if (hasParameters)
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
if (forUpdate_)
|
||
|
{
|
||
|
sql += " for update";
|
||
|
}
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
if (limit_ > 0)
|
||
|
binder << limit_;
|
||
|
if (offset_)
|
||
|
binder << offset_;
|
||
|
clear();
|
||
|
std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();
|
||
|
binder >> [prom](const Result &r) {
|
||
|
if (r.size() == 0)
|
||
|
{
|
||
|
prom->set_exception(
|
||
|
std::make_exception_ptr(UnexpectedRows("0 rows found")));
|
||
|
}
|
||
|
else if (r.size() > 1)
|
||
|
{
|
||
|
prom->set_exception(std::make_exception_ptr(
|
||
|
UnexpectedRows("Found more than one row")));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
prom->set_value(T(r[0]));
|
||
|
}
|
||
|
};
|
||
|
binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline std::vector<T> Mapper<T>::findBy(const Criteria &criteria) noexcept(
|
||
|
false)
|
||
|
{
|
||
|
std::string sql = "select * from ";
|
||
|
sql += T::tableName;
|
||
|
bool hasParameters = false;
|
||
|
if (criteria)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
}
|
||
|
sql.append(orderByString_);
|
||
|
if (limit_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" limit $?");
|
||
|
}
|
||
|
if (offset_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" offset $?");
|
||
|
}
|
||
|
if (hasParameters)
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
if (forUpdate_)
|
||
|
{
|
||
|
sql += " for update";
|
||
|
}
|
||
|
Result r(nullptr);
|
||
|
{
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
if (limit_ > 0)
|
||
|
binder << limit_;
|
||
|
if (offset_)
|
||
|
binder << offset_;
|
||
|
clear();
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // exec may be throw exception;
|
||
|
}
|
||
|
std::vector<T> ret;
|
||
|
for (auto const &row : r)
|
||
|
{
|
||
|
ret.push_back(T(row));
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::findBy(const Criteria &criteria,
|
||
|
const MultipleRowsCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
std::string sql = "select * from ";
|
||
|
sql += T::tableName;
|
||
|
bool hasParameters = false;
|
||
|
if (criteria)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
}
|
||
|
sql.append(orderByString_);
|
||
|
if (limit_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" limit $?");
|
||
|
}
|
||
|
if (offset_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" offset $?");
|
||
|
}
|
||
|
if (hasParameters)
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
if (forUpdate_)
|
||
|
{
|
||
|
sql += " for update";
|
||
|
}
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
if (limit_ > 0)
|
||
|
binder << limit_;
|
||
|
if (offset_)
|
||
|
binder << offset_;
|
||
|
clear();
|
||
|
binder >> [rcb](const Result &r) {
|
||
|
std::vector<T> ret;
|
||
|
for (auto const &row : r)
|
||
|
{
|
||
|
ret.push_back(T(row));
|
||
|
}
|
||
|
rcb(ret);
|
||
|
};
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline std::future<std::vector<T>> Mapper<T>::findFutureBy(
|
||
|
const Criteria &criteria) noexcept
|
||
|
{
|
||
|
std::string sql = "select * from ";
|
||
|
sql += T::tableName;
|
||
|
bool hasParameters = false;
|
||
|
if (criteria)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
}
|
||
|
sql.append(orderByString_);
|
||
|
if (limit_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" limit $?");
|
||
|
}
|
||
|
if (offset_ > 0)
|
||
|
{
|
||
|
hasParameters = true;
|
||
|
sql.append(" offset $?");
|
||
|
}
|
||
|
if (hasParameters)
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
if (forUpdate_)
|
||
|
{
|
||
|
sql += " for update";
|
||
|
}
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
if (limit_ > 0)
|
||
|
binder << limit_;
|
||
|
if (offset_)
|
||
|
binder << offset_;
|
||
|
clear();
|
||
|
std::shared_ptr<std::promise<std::vector<T>>> prom =
|
||
|
std::make_shared<std::promise<std::vector<T>>>();
|
||
|
binder >> [prom](const Result &r) {
|
||
|
std::vector<T> ret;
|
||
|
for (auto const &row : r)
|
||
|
{
|
||
|
ret.push_back(T(row));
|
||
|
}
|
||
|
prom->set_value(ret);
|
||
|
};
|
||
|
binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline std::vector<T> Mapper<T>::findAll() noexcept(false)
|
||
|
{
|
||
|
return findBy(Criteria());
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::findAll(const MultipleRowsCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
findBy(Criteria(), rcb, ecb);
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline std::future<std::vector<T>> Mapper<T>::findFutureAll() noexcept
|
||
|
{
|
||
|
return findFutureBy(Criteria());
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline size_t Mapper<T>::count(const Criteria &criteria) noexcept(false)
|
||
|
{
|
||
|
std::string sql = "select count(*) from ";
|
||
|
sql += T::tableName;
|
||
|
if (criteria)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
}
|
||
|
clear();
|
||
|
Result r(nullptr);
|
||
|
{
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // exec may be throw exception;
|
||
|
}
|
||
|
assert(r.size() == 1);
|
||
|
return r[0][(Row::SizeType)0].as<size_t>();
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::count(const Criteria &criteria,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
std::string sql = "select count(*) from ";
|
||
|
sql += T::tableName;
|
||
|
if (criteria)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
}
|
||
|
clear();
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
binder >> [rcb](const Result &r) {
|
||
|
assert(r.size() == 1);
|
||
|
rcb(r[0][(Row::SizeType)0].as<size_t>());
|
||
|
};
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline std::future<size_t> Mapper<T>::countFuture(
|
||
|
const Criteria &criteria) noexcept
|
||
|
{
|
||
|
std::string sql = "select count(*) from ";
|
||
|
sql += T::tableName;
|
||
|
if (criteria)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
}
|
||
|
clear();
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
|
||
|
std::shared_ptr<std::promise<size_t>> prom =
|
||
|
std::make_shared<std::promise<size_t>>();
|
||
|
binder >> [prom](const Result &r) {
|
||
|
assert(r.size() == 1);
|
||
|
prom->set_value(r[0][(Row::SizeType)0].as<size_t>());
|
||
|
};
|
||
|
binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::insert(T &obj) noexcept(false)
|
||
|
{
|
||
|
clear();
|
||
|
Result r(nullptr);
|
||
|
bool needSelection = false;
|
||
|
{
|
||
|
auto binder = *client_ << obj.sqlForInserting(needSelection);
|
||
|
obj.outputArgs(binder);
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // Maybe throw exception;
|
||
|
}
|
||
|
assert(r.affectedRows() == 1);
|
||
|
if (client_->type() == ClientType::PostgreSQL)
|
||
|
{
|
||
|
if (needSelection)
|
||
|
{
|
||
|
assert(r.size() == 1);
|
||
|
obj = T(r[0]);
|
||
|
}
|
||
|
}
|
||
|
else // Mysql or Sqlite3
|
||
|
{
|
||
|
auto id = r.insertId();
|
||
|
obj.updateId(id);
|
||
|
if (needSelection)
|
||
|
{
|
||
|
obj = findByPrimaryKey(obj.getPrimaryKey());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::insert(const T &obj,
|
||
|
const SingleRowCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
clear();
|
||
|
bool needSelection = false;
|
||
|
auto binder = *client_ << obj.sqlForInserting(needSelection);
|
||
|
obj.outputArgs(binder);
|
||
|
auto client = client_;
|
||
|
binder >> [client, rcb, obj, needSelection, ecb](const Result &r) {
|
||
|
assert(r.affectedRows() == 1);
|
||
|
if (client->type() == ClientType::PostgreSQL)
|
||
|
{
|
||
|
if (needSelection)
|
||
|
{
|
||
|
assert(r.size() == 1);
|
||
|
rcb(T(r[0]));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rcb(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(), rcb, ecb);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rcb(newObj);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline std::future<T> Mapper<T>::insertFuture(const T &obj) noexcept
|
||
|
{
|
||
|
clear();
|
||
|
bool needSelection = false;
|
||
|
auto binder = *client_ << obj.sqlForInserting(needSelection);
|
||
|
obj.outputArgs(binder);
|
||
|
|
||
|
std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();
|
||
|
auto client = client_;
|
||
|
binder >> [client, prom, obj, needSelection](const Result &r) {
|
||
|
assert(r.affectedRows() == 1);
|
||
|
if (client->type() == ClientType::PostgreSQL)
|
||
|
{
|
||
|
if (needSelection)
|
||
|
{
|
||
|
assert(r.size() == 1);
|
||
|
prom->set_value(T(r[0]));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
prom->set_value(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(),
|
||
|
[prom](T selObj) { prom->set_value(selObj); },
|
||
|
[prom](const DrogonDbException &e) {
|
||
|
prom->set_exception(
|
||
|
std::make_exception_ptr(Failure(e.base().what())));
|
||
|
});
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
prom->set_value(newObj);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline size_t Mapper<T>::update(const T &obj) noexcept(false)
|
||
|
{
|
||
|
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 ','
|
||
|
|
||
|
makePrimaryKeyCriteria(sql);
|
||
|
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
Result r(nullptr);
|
||
|
{
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
obj.updateArgs(binder);
|
||
|
outputPrimeryKeyToBinder(obj.getPrimaryKey(), binder);
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // Maybe throw exception;
|
||
|
}
|
||
|
return r.affectedRows();
|
||
|
}
|
||
|
template <typename T>
|
||
|
template <typename... Arguments>
|
||
|
size_t Mapper<T>::updateBy(const std::vector<std::string> &colNames,
|
||
|
const Criteria &criteria,
|
||
|
Arguments &&...args) noexcept(false)
|
||
|
{
|
||
|
static_assert(sizeof...(args) > 0);
|
||
|
assert(colNames.size() == sizeof...(args));
|
||
|
clear();
|
||
|
std::string sql = "update ";
|
||
|
sql += T::tableName;
|
||
|
sql += " set ";
|
||
|
for (auto const &colName : colNames)
|
||
|
{
|
||
|
sql += colName;
|
||
|
sql += " = $?,";
|
||
|
}
|
||
|
sql[sql.length() - 1] = ' '; // Replace the last ','
|
||
|
|
||
|
if (criteria)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
}
|
||
|
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
Result r(nullptr);
|
||
|
{
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
(void)std::initializer_list<int>{
|
||
|
(binder << std::forward<Arguments>(args), 0)...};
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // Maybe throw exception;
|
||
|
}
|
||
|
return r.affectedRows();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::update(const T &obj,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
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 ','
|
||
|
|
||
|
makePrimaryKeyCriteria(sql);
|
||
|
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
obj.updateArgs(binder);
|
||
|
outputPrimeryKeyToBinder(obj.getPrimaryKey(), binder);
|
||
|
binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
template <typename T>
|
||
|
template <typename... Arguments>
|
||
|
void Mapper<T>::updateBy(const std::vector<std::string> &colNames,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb,
|
||
|
const Criteria &criteria,
|
||
|
Arguments &&...args) noexcept
|
||
|
{
|
||
|
static_assert(sizeof...(args) > 0);
|
||
|
assert(colNames.size() == sizeof...(args));
|
||
|
clear();
|
||
|
std::string sql = "update ";
|
||
|
sql += T::tableName;
|
||
|
sql += " set ";
|
||
|
for (auto const &colName : colNames)
|
||
|
{
|
||
|
sql += colName;
|
||
|
sql += " = $?,";
|
||
|
}
|
||
|
sql[sql.length() - 1] = ' '; // Replace the last ','
|
||
|
|
||
|
if (criteria)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
}
|
||
|
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
(void)std::initializer_list<int>{
|
||
|
(binder << std::forward<Arguments>(args), 0)...};
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline std::future<size_t> Mapper<T>::updateFuture(const T &obj) noexcept
|
||
|
{
|
||
|
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 ','
|
||
|
|
||
|
makePrimaryKeyCriteria(sql);
|
||
|
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
obj.updateArgs(binder);
|
||
|
outputPrimeryKeyToBinder(obj.getPrimaryKey(), binder);
|
||
|
|
||
|
std::shared_ptr<std::promise<size_t>> prom =
|
||
|
std::make_shared<std::promise<size_t>>();
|
||
|
binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
|
||
|
binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
template <typename T>
|
||
|
template <typename... Arguments>
|
||
|
inline std::future<size_t> Mapper<T>::updateFutureBy(
|
||
|
const std::vector<std::string> &colNames,
|
||
|
const Criteria &criteria,
|
||
|
Arguments &&...args) noexcept
|
||
|
{
|
||
|
static_assert(sizeof...(args) > 0);
|
||
|
assert(colNames.size() == sizeof...(args));
|
||
|
clear();
|
||
|
std::string sql = "update ";
|
||
|
sql += T::tableName;
|
||
|
sql += " set ";
|
||
|
for (auto const &colName : colNames)
|
||
|
{
|
||
|
sql += colName;
|
||
|
sql += " = $?,";
|
||
|
}
|
||
|
sql[sql.length() - 1] = ' '; // Replace the last ','
|
||
|
|
||
|
if (criteria)
|
||
|
{
|
||
|
sql += " where ";
|
||
|
sql += criteria.criteriaString();
|
||
|
}
|
||
|
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
(void)std::initializer_list<int>{
|
||
|
(binder << std::forward<Arguments>(args), 0)...};
|
||
|
if (criteria)
|
||
|
criteria.outputArgs(binder);
|
||
|
|
||
|
std::shared_ptr<std::promise<size_t>> prom =
|
||
|
std::make_shared<std::promise<size_t>>();
|
||
|
binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
|
||
|
binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline size_t Mapper<T>::deleteOne(const T &obj) noexcept(false)
|
||
|
{
|
||
|
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 += " "; // Replace the last ','
|
||
|
|
||
|
makePrimaryKeyCriteria(sql);
|
||
|
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
Result r(nullptr);
|
||
|
{
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
outputPrimeryKeyToBinder(obj.getPrimaryKey(), binder);
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // Maybe throw exception;
|
||
|
}
|
||
|
return r.affectedRows();
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::deleteOne(const T &obj,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
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 += " ";
|
||
|
|
||
|
makePrimaryKeyCriteria(sql);
|
||
|
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
outputPrimeryKeyToBinder(obj.getPrimaryKey(), binder);
|
||
|
binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline std::future<size_t> Mapper<T>::deleteFutureOne(const T &obj) noexcept
|
||
|
{
|
||
|
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 += " ";
|
||
|
|
||
|
makePrimaryKeyCriteria(sql);
|
||
|
|
||
|
sql = replaceSqlPlaceHolder(sql, "$?");
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
outputPrimeryKeyToBinder(obj.getPrimaryKey(), binder);
|
||
|
|
||
|
std::shared_ptr<std::promise<size_t>> prom =
|
||
|
std::make_shared<std::promise<size_t>>();
|
||
|
binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
|
||
|
binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline size_t Mapper<T>::deleteBy(const Criteria &criteria) noexcept(false)
|
||
|
{
|
||
|
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 = replaceSqlPlaceHolder(sql, "$?");
|
||
|
}
|
||
|
|
||
|
Result r(nullptr);
|
||
|
{
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
{
|
||
|
criteria.outputArgs(binder);
|
||
|
}
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // Maybe throw exception;
|
||
|
}
|
||
|
return r.affectedRows();
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::deleteBy(const Criteria &criteria,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
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 = replaceSqlPlaceHolder(sql, "$?");
|
||
|
}
|
||
|
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
{
|
||
|
criteria.outputArgs(binder);
|
||
|
}
|
||
|
binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline std::future<size_t> Mapper<T>::deleteFutureBy(
|
||
|
const Criteria &criteria) noexcept
|
||
|
{
|
||
|
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 = replaceSqlPlaceHolder(sql, "$?");
|
||
|
}
|
||
|
auto binder = *client_ << std::move(sql);
|
||
|
if (criteria)
|
||
|
{
|
||
|
criteria.outputArgs(binder);
|
||
|
}
|
||
|
|
||
|
std::shared_ptr<std::promise<size_t>> prom =
|
||
|
std::make_shared<std::promise<size_t>>();
|
||
|
binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
|
||
|
binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline Mapper<T> &Mapper<T>::limit(size_t limit)
|
||
|
{
|
||
|
assert(limit > 0);
|
||
|
limit_ = limit;
|
||
|
return *this;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline Mapper<T> &Mapper<T>::offset(size_t offset)
|
||
|
{
|
||
|
offset_ = offset;
|
||
|
return *this;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline Mapper<T> &Mapper<T>::orderBy(const std::string &colName,
|
||
|
const SortOrder &order)
|
||
|
{
|
||
|
if (orderByString_.empty())
|
||
|
{
|
||
|
orderByString_ =
|
||
|
utils::formattedString(" order by %s", colName.c_str());
|
||
|
if (order == SortOrder::DESC)
|
||
|
{
|
||
|
orderByString_ += " desc";
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
orderByString_ += ",";
|
||
|
orderByString_ += colName;
|
||
|
if (order == SortOrder::DESC)
|
||
|
{
|
||
|
orderByString_ += " desc";
|
||
|
}
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline Mapper<T> &Mapper<T>::orderBy(size_t colIndex, const SortOrder &order)
|
||
|
{
|
||
|
std::string colName = T::getColumnName(colIndex);
|
||
|
assert(!colName.empty());
|
||
|
return orderBy(colName, order);
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline Mapper<T> &Mapper<T>::paginate(size_t page, size_t perPage)
|
||
|
{
|
||
|
assert(page > 0 && perPage > 0);
|
||
|
return limit(perPage).offset((page - 1) * perPage);
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline Mapper<T> &Mapper<T>::forUpdate()
|
||
|
{
|
||
|
forUpdate_ = true;
|
||
|
return *this;
|
||
|
}
|
||
|
template <typename T>
|
||
|
inline std::string Mapper<T>::replaceSqlPlaceHolder(
|
||
|
const std::string &sqlStr,
|
||
|
const std::string &holderStr) const
|
||
|
{
|
||
|
if (client_->type() == ClientType::PostgreSQL)
|
||
|
{
|
||
|
std::string::size_type startPos = 0;
|
||
|
std::string::size_type pos;
|
||
|
std::stringstream ret;
|
||
|
size_t phCount = 1;
|
||
|
do
|
||
|
{
|
||
|
pos = sqlStr.find(holderStr, startPos);
|
||
|
if (pos == std::string::npos)
|
||
|
{
|
||
|
ret << sqlStr.substr(startPos);
|
||
|
return ret.str();
|
||
|
}
|
||
|
ret << sqlStr.substr(startPos, pos - startPos);
|
||
|
ret << "$";
|
||
|
ret << phCount++;
|
||
|
startPos = pos + holderStr.length();
|
||
|
} while (1);
|
||
|
}
|
||
|
else if (client_->type() == ClientType::Mysql ||
|
||
|
client_->type() == ClientType::Sqlite3)
|
||
|
{
|
||
|
std::string::size_type startPos = 0;
|
||
|
std::string::size_type pos;
|
||
|
std::stringstream ret;
|
||
|
do
|
||
|
{
|
||
|
pos = sqlStr.find(holderStr, startPos);
|
||
|
if (pos == std::string::npos)
|
||
|
{
|
||
|
ret << sqlStr.substr(startPos);
|
||
|
return ret.str();
|
||
|
}
|
||
|
ret << sqlStr.substr(startPos, pos - startPos);
|
||
|
ret << "?";
|
||
|
startPos = pos + holderStr.length();
|
||
|
} while (1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return sqlStr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline size_t Mapper<T>::deleteByPrimaryKey(
|
||
|
const typename Mapper<T>::TraitsPKType &key) noexcept(false)
|
||
|
{
|
||
|
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");
|
||
|
clear();
|
||
|
Result r(nullptr);
|
||
|
{
|
||
|
auto binder = *client_ << T::sqlForDeletingByPrimaryKey();
|
||
|
outputPrimeryKeyToBinder(key, binder);
|
||
|
binder << Mode::Blocking;
|
||
|
binder >> [&r](const Result &result) { r = result; };
|
||
|
binder.exec(); // exec may be throw exception;
|
||
|
}
|
||
|
return r.affectedRows();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline void Mapper<T>::deleteByPrimaryKey(
|
||
|
const typename Mapper<T>::TraitsPKType &key,
|
||
|
const CountCallback &rcb,
|
||
|
const ExceptionCallback &ecb) noexcept
|
||
|
{
|
||
|
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");
|
||
|
clear();
|
||
|
auto binder = *client_ << T::sqlForDeletingByPrimaryKey();
|
||
|
outputPrimeryKeyToBinder(key, binder);
|
||
|
binder >>
|
||
|
[rcb = std::move(rcb)](const Result &r) { rcb(r.affectedRows()); };
|
||
|
binder >> ecb;
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
inline std::future<size_t> Mapper<T>::deleteFutureByPrimaryKey(
|
||
|
const typename Mapper<T>::TraitsPKType &key) noexcept
|
||
|
{
|
||
|
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");
|
||
|
clear();
|
||
|
auto binder = *client_ << T::sqlForDeletingByPrimaryKey();
|
||
|
outputPrimeryKeyToBinder(key, binder);
|
||
|
|
||
|
std::shared_ptr<std::promise<size_t>> prom =
|
||
|
std::make_shared<std::promise<size_t>>();
|
||
|
binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
|
||
|
binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
|
||
|
binder.exec();
|
||
|
return prom->get_future();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Convert std::vector<Value> where Value can be JSON to JSON.
|
||
|
*
|
||
|
* @param container The container to be converted.
|
||
|
* @return Json::Value The JSON value converted.
|
||
|
*/
|
||
|
template <typename Value>
|
||
|
inline Json::Value toJson(const std::vector<Value> &container)
|
||
|
{
|
||
|
Json::Value values(Json::arrayValue);
|
||
|
for (const Value &c : container)
|
||
|
{
|
||
|
values.append(c.toJson());
|
||
|
}
|
||
|
return values;
|
||
|
}
|
||
|
|
||
|
} // namespace orm
|
||
|
} // namespace drogon
|