Implement QueryBuilder (#1331)

This commit is contained in:
Ken Matsui 2023-05-21 18:52:19 -07:00 committed by GitHub
parent 7fce0f1ff5
commit 75b106599d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1121 additions and 1 deletions

View File

@ -14,6 +14,7 @@ using namespace drogon_ctl;
#include <drogon/orm/Field.h>
#include <drogon/orm/SqlBinder.h>
#include <drogon/orm/Mapper.h>
#include <drogon/orm/BaseBuilder.h>
#ifdef __cpp_impl_coroutine
#include <drogon/orm/CoroMapper.h>
#endif
@ -303,6 +304,10 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
%>
private:
friend drogon::orm::Mapper<[[className]]>;
friend drogon::orm::BaseBuilder<[[className]], true, true>;
friend drogon::orm::BaseBuilder<[[className]], true, false>;
friend drogon::orm::BaseBuilder<[[className]], false, true>;
friend drogon::orm::BaseBuilder<[[className]], false, false>;
#ifdef __cpp_impl_coroutine
friend drogon::orm::CoroMapper<[[className]]>;
#endif

View File

@ -0,0 +1,290 @@
/**
*
* @file BaseBuilder.h
* @author Ken Matsui
*
* Copyright 2022, Ken Matsui. All rights reserved.
* https://github.com/drogonframework/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/optional.h>
#include <drogon/utils/string_view.h>
#include <future>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#define unimplemented() assert(false && "unimplemented")
namespace drogon
{
namespace orm
{
inline std::string to_string(CompareOperator op)
{
switch (op)
{
case CompareOperator::EQ:
return "=";
case CompareOperator::NE:
return "!=";
case CompareOperator::GT:
return ">";
case CompareOperator::GE:
return ">=";
case CompareOperator::LT:
return "<";
case CompareOperator::LE:
return "<=";
case CompareOperator::Like:
return "like";
case CompareOperator::NotLike:
case CompareOperator::In:
case CompareOperator::NotIn:
case CompareOperator::IsNull:
case CompareOperator::IsNotNull:
default:
unimplemented();
return "";
}
}
struct Filter
{
std::string column;
CompareOperator op;
std::string value;
};
// Forward declaration to be a friend
template <typename T, bool SelectAll, bool Single = false>
class TransformBuilder;
template <typename T, bool SelectAll, bool Single = false>
class BaseBuilder
{
using ResultType =
std::conditional_t<SelectAll,
std::conditional_t<Single, T, std::vector<T>>,
std::conditional_t<Single, Row, Result>>;
// Make the constructor of `TransformBuilder<T, SelectAll, true>` through
// `TransformBuilder::single()` be able to read these protected members.
friend class TransformBuilder<T, SelectAll, true>;
protected:
std::string from_;
std::string columns_;
std::vector<Filter> filters_;
optional<std::uint64_t> limit_;
optional<std::uint64_t> offset_;
// The order is important; use vector<pair> instead of unordered_map and
// map.
std::vector<std::pair<std::string, bool>> orders_;
inline void assert_column(const std::string& colName) const
{
for (const typename T::MetaData& m : T::metaData_)
{
if (m.colName_ == colName)
{
return;
}
}
throw UsageError("The column `" + colName +
"` is not in the specified table.");
}
private:
/**
* @brief Generate SQL query in string.
*
* @return std::string The string generated SQL query.
*/
inline std::string gen_sql(ClientType type) const noexcept
{
int pCount = 0;
const auto placeholder = [type, &pCount]() {
++pCount;
return type == ClientType::PostgreSQL ? "$" + std::to_string(pCount)
: "?";
};
std::string sql = "select " + columns_ + " from " + from_;
if (!filters_.empty())
{
sql += " where " + filters_[0].column + " " +
to_string(filters_[0].op) + " " + placeholder() + "";
for (int i = 1; i < filters_.size(); ++i)
{
sql += " and " + filters_[i].column + " " +
to_string(filters_[i].op) + " " + placeholder() + "";
}
}
if (!orders_.empty())
{
sql += " order by " + orders_[0].first + " " +
std::string(orders_[0].second ? "asc" : "desc");
for (int i = 1; i < orders_.size(); ++i)
{
sql += ", " + orders_[i].first + " " +
std::string(orders_[i].second ? "asc" : "desc");
}
}
if (limit_.has_value())
{
sql += " limit " + std::to_string(limit_.value());
}
if (offset_.has_value())
{
sql += " offset " + std::to_string(offset_.value());
}
return sql;
}
inline std::vector<std::string> gen_args() const noexcept
{
std::vector<std::string> args;
if (!filters_.empty())
{
for (const Filter& f : filters_)
{
args.emplace_back(f.value);
}
}
return args;
}
public:
#ifdef __cpp_if_constexpr
static ResultType convert_result(const Result& r)
{
if constexpr (SelectAll)
{
if constexpr (Single)
{
return T(r[0]);
}
else
{
std::vector<T> ret;
for (const Row& row : r)
{
ret.emplace_back(T(row));
}
return ret;
}
}
else
{
if constexpr (Single)
{
return r[0];
}
else
{
return r;
}
}
}
#else
template <bool SA = SelectAll,
bool SI = Single,
std::enable_if_t<SA, std::nullptr_t> = nullptr,
std::enable_if_t<SI, std::nullptr_t> = nullptr>
static inline T convert_result(const Result& r)
{
return T(r[0]);
}
template <bool SA = SelectAll,
bool SI = Single,
std::enable_if_t<SA, std::nullptr_t> = nullptr,
std::enable_if_t<!SI, std::nullptr_t> = nullptr>
static inline std::vector<T> convert_result(const Result& r)
{
std::vector<T> ret;
for (const Row& row : r)
{
ret.template emplace_back(T(row));
}
return ret;
}
template <bool SA = SelectAll,
bool SI = Single,
std::enable_if_t<!SA, std::nullptr_t> = nullptr,
std::enable_if_t<SI, std::nullptr_t> = nullptr>
static inline Row convert_result(const Result& r)
{
return r[0];
}
template <bool SA = SelectAll,
bool SI = Single,
std::enable_if_t<!SA, std::nullptr_t> = nullptr,
std::enable_if_t<!SI, std::nullptr_t> = nullptr>
static inline Result convert_result(const Result& r)
{
return r;
}
#endif
inline ResultType execSync(const DbClientPtr& client)
{
Result r(nullptr);
{
auto binder = *client << gen_sql(client->type());
for (const std::string& a : gen_args())
{
binder << a;
}
binder << Mode::Blocking;
binder >> [&r](const Result& result) { r = result; };
binder.exec(); // exec may throw exception
}
return convert_result(r);
}
template <typename TFn, typename EFn>
void execAsync(const DbClientPtr& client,
TFn&& rCallback,
EFn&& exceptCallback) noexcept
{
auto binder = *client << gen_sql(client->type());
for (const std::string& a : gen_args())
{
binder << a;
}
binder >> std::forward<TFn>(rCallback);
binder >> std::forward<EFn>(exceptCallback);
}
inline std::future<ResultType> execAsyncFuture(
const DbClientPtr& client) noexcept
{
auto binder = *client << gen_sql(client->type());
for (const std::string& a : gen_args())
{
binder << a;
}
std::shared_ptr<std::promise<ResultType>> prom =
std::make_shared<std::promise<ResultType>>();
binder >>
[prom](const Result& r) { prom->set_value(convert_result(r)); };
binder >>
[prom](const std::exception_ptr& e) { prom->set_exception(e); };
binder.exec();
return prom->get_future();
}
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,162 @@
/**
*
* @file FilterBuilder.h
* @author Ken Matsui
*
* Copyright 2022, Ken Matsui. All rights reserved.
* https://github.com/drogonframework/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/TransformBuilder.h>
#include <string>
namespace drogon
{
namespace orm
{
template <typename T, bool SelectAll>
class FilterBuilder : public TransformBuilder<T, SelectAll, false>
{
public:
/**
* @brief A default constructor for derived classes.
*
* @return FilterBuilder The FilterBuilder itself.
*/
FilterBuilder() = default;
/**
* @brief A copy constructor to be called by QueryBuilder.
*
* @param from The table.
* @param columns The columns.
*
* @return FilterBuilder The FilterBuilder itself.
*/
FilterBuilder(const std::string& from, const std::string& columns)
{
this->from_ = from;
this->columns_ = columns;
}
/**
* @brief Filter rows whose value is the same as `value`.
*
* @param column The column to be filtered.
* @param value The value to filter rows.
*
* @return FilterBuilder& The FilterBuilder itself.
*/
inline FilterBuilder& eq(const std::string& column,
const std::string& value)
{
this->assert_column(column);
this->filters_.push_back({column, CompareOperator::EQ, value});
return *this;
}
/**
* @brief Filter rows whose value is NOT the same as `value`.
*
* @param column The column to be filtered.
* @param value The value to filter rows.
*
* @return FilterBuilder& The FilterBuilder itself.
*/
inline FilterBuilder& neq(const std::string& column,
const std::string& value)
{
this->assert_column(column);
this->filters_.push_back({column, CompareOperator::NE, value});
return *this;
}
/**
* @brief Filter rows whose value is greater than `value`.
*
* @param column The column to be filtered.
* @param value The value to filter rows.
*
* @return FilterBuilder& The FilterBuilder itself.
*/
inline FilterBuilder& gt(const std::string& column,
const std::string& value)
{
this->assert_column(column);
this->filters_.push_back({column, CompareOperator::GT, value});
return *this;
}
/**
* @brief Filter rows whose value is greater than or equal to `value`.
*
* @param column The column to be filtered.
* @param value The value to filter rows.
*
* @return FilterBuilder& The FilterBuilder itself.
*/
inline FilterBuilder& gte(const std::string& column,
const std::string& value)
{
this->assert_column(column);
this->filters_.push_back({column, CompareOperator::GE, value});
return *this;
}
/**
* @brief Filter rows whose value is less than `value`.
*
* @param column The column to be filtered.
* @param value The value to filter rows.
*
* @return FilterBuilder& The FilterBuilder itself.
*/
inline FilterBuilder& lt(const std::string& column,
const std::string& value)
{
this->assert_column(column);
this->filters_.push_back({column, CompareOperator::LT, value});
return *this;
}
/**
* @brief Filter rows whose value is less than or equal to `value`.
*
* @param column The column to be filtered.
* @param value The value to filter rows.
*
* @return FilterBuilder& The FilterBuilder itself.
*/
inline FilterBuilder& lte(const std::string& column,
const std::string& value)
{
this->assert_column(column);
this->filters_.push_back({column, CompareOperator::LE, value});
return *this;
}
/**
* @brief Filter rows whose value matches the `pattern`.
*
* @param column The column to be filtered.
* @param pattern The pattern to filter rows.
*
* @return FilterBuilder& The FilterBuilder itself.
*/
inline FilterBuilder& like(const std::string& column,
const std::string& pattern)
{
this->assert_column(column);
this->filters_.push_back({column, CompareOperator::Like, pattern});
return *this;
}
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,78 @@
/**
*
* @file QueryBuilder.h
* @author Ken Matsui
*
* Copyright 2022, Ken Matsui. All rights reserved.
* https://github.com/drogonframework/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/FilterBuilder.h>
#include <string>
namespace drogon
{
namespace orm
{
template <typename T>
class QueryBuilder : public FilterBuilder<T, true>
{
/**
* @brief When a user does not set the table name explicitly, then retrieve
* it from model `T`.
*
* @return std::string The table name
*/
inline const std::string& getTableName() const
{
return this->from_.empty() ? T::tableName : this->from_;
}
public:
/**
* @brief Set from which table to return.
*
* @param table The table.
*
* @return QueryBuilder& The QueryBuilder itself.
*/
inline QueryBuilder& from(const std::string& table)
{
this->from_ = table;
return *this;
}
/**
* @brief Select specific columns.
*
* @param columns The columns.
*
* @return FilterBuilder<T, false> A new FilterBuilder.
*
* @note If you would return all rows, please use the `selectAll` method.
* The method can return rows as model type `T`.
*/
inline FilterBuilder<T, false> select(const std::string& columns) const
{
return {getTableName(), columns};
}
/**
* @brief Select all columns.
*
* @return FilterBuilder<T, true> A new FilterBuilder.
*/
inline FilterBuilder<T, true> selectAll() const
{
return {getTableName(), "*"};
}
};
} // namespace orm
} // namespace drogon

View File

@ -77,11 +77,13 @@ class DROGON_EXPORT Row
ConstReverseIterator rend() const;
ConstReverseIterator crend() const;
Row() noexcept = default;
Row(const Row &r) noexcept = default;
Row(Row &&) noexcept = default;
Row &operator=(const Row &) = default;
private:
const Result result_;
Result result_;
protected:
friend class Field;

View File

@ -0,0 +1,127 @@
/**
*
* @file TransformBuilder.h
* @author Ken Matsui
*
* Copyright 2022, Ken Matsui. All rights reserved.
* https://github.com/drogonframework/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/BaseBuilder.h>
#include <string>
#include <type_traits>
namespace drogon
{
namespace orm
{
template <typename T, bool SelectAll, bool Single>
class TransformBuilder : public BaseBuilder<T, SelectAll, Single>
{
public:
/**
* @brief A default constructor for derived classes.
*
* @return TransformBuilder The TransformBuilder itself.
*/
TransformBuilder() = default;
/**
* @brief A copy constructor from a non `Single` builder to `Single`
* builder, used by the `single` method.
*
* @return TransformBuilder The TransformBuilder itself.
*
* @note This function is enabled only when `Single` is true.
*/
template <bool SI = Single, std::enable_if_t<SI, std::nullptr_t> = nullptr>
TransformBuilder(const TransformBuilder<T, SelectAll, false>& tb)
{
this->from_ = tb.from_;
this->columns_ = tb.columns_;
this->filters_ = tb.filters_;
this->limit_ = tb.limit_;
this->offset_ = tb.offset_;
this->orders_ = tb.orders_;
}
/**
* @brief Limit the result to `count`.
*
* @param count The number of rows to be limited.
*
* @return TransformBuilder& The TransformBuilder itself.
*/
inline TransformBuilder& limit(std::uint64_t count)
{
this->limit_ = count;
return *this;
}
/**
* @brief Add a offset to the query.
*
* @param offset The offset.
*
* @return TransformBuilder& The TransformBuilder itself.
*/
inline TransformBuilder& offset(std::uint64_t count)
{
this->offset_ = count;
return *this;
}
/**
* @brief Limit the result to an inclusive range.
*
* @param from The first index to limit the result.
* @param to The last index to limit the result.
*
* @return TransformBuilder& The TransformBuilder itself.
*/
inline TransformBuilder& range(std::uint64_t from, std::uint64_t to)
{
this->offset_ = from;
this->limit_ = to - from + 1; // inclusive
return *this;
}
/**
* @brief Order the result.
*
* @param column The column to order by.
* @param asc If `true`, ascending order. If `false`, descending order.
*
* @return TransformBuilder& The TransformBuilder itself.
*/
inline TransformBuilder& order(const std::string& column, bool asc = true)
{
this->assert_column(column);
this->orders_.emplace_back(column, asc);
return *this;
}
/**
* @brief Ensure returning only one row.
*
* @return TransformBuilder<T, SelectAll, true> The TransformBuilder where
* Single is true and all else is the same.
*
* @note This function can be called only once throughout an instance of a
* builder.
*/
template <bool SI = Single, std::enable_if_t<!SI, std::nullptr_t> = nullptr>
inline TransformBuilder<T, SelectAll, true> single() const
{
return {*this};
}
};
} // namespace orm
} // namespace drogon

View File

@ -20,6 +20,7 @@
#include <drogon/orm/DbClient.h>
#include <drogon/orm/DbTypes.h>
#include <drogon/utils/string_view.h>
#include <drogon/orm/QueryBuilder.h>
#include <trantor/utils/Logger.h>
#include <stdlib.h>
@ -682,6 +683,152 @@ DROGON_TEST(PostgreTest)
"salt of pg");
}
/// Test ORM QueryBuilder
/// execSync
try
{
const std::vector<Users> users =
QueryBuilder<Users>{}.from("users").selectAll().execSync(clientPtr);
MANDATE(users.size() == 3);
}
catch (const DrogonDbException &e)
{
FAULT("postgresql - ORM QueryBuilder synchronous interface(0) what():",
e.base().what());
}
try
{
const Result users =
QueryBuilder<Users>{}.from("users").select("id").execSync(
clientPtr);
MANDATE(users.size() == 3);
for (const Row &u : users)
{
MANDATE(!u["id"].isNull());
}
}
catch (const DrogonDbException &e)
{
FAULT("postgresql - ORM QueryBuilder synchronous interface(1) what():",
e.base().what());
}
try
{
const Users user = QueryBuilder<Users>{}
.from("users")
.selectAll()
.eq("id", "3")
.limit(1)
.single()
.order("id", false)
.execSync(clientPtr);
MANDATE(user.getPrimaryKey() == 3);
}
catch (const DrogonDbException &e)
{
FAULT("postgresql - ORM QueryBuilder synchronous interface(2) what():",
e.base().what());
}
try
{
const Row user = QueryBuilder<Users>{}
.from("users")
.select("id")
.limit(1)
.single()
.order("id", false)
.execSync(clientPtr);
MANDATE(user["id"].as<int32_t>() == 3);
}
catch (const DrogonDbException &e)
{
FAULT("postgresql - ORM QueryBuilder synchronous interface(3) what():",
e.base().what());
}
/// execAsyncFuture
{
std::future<std::vector<Users>> users =
QueryBuilder<Users>{}.from("users").selectAll().execAsyncFuture(
clientPtr);
try
{
const std::vector<Users> r = users.get();
MANDATE(r.size() == 3);
}
catch (const DrogonDbException &e)
{
FAULT(
"postgresql - ORM QueryBuilder asynchronous interface(0) "
"what():",
e.base().what());
}
}
{
std::future<Result> users =
QueryBuilder<Users>{}.from("users").select("id").execAsyncFuture(
clientPtr);
try
{
const Result r = users.get();
MANDATE(r.size() == 3);
for (const Row &u : r)
{
MANDATE(!u["id"].isNull());
}
}
catch (const DrogonDbException &e)
{
FAULT(
"postgresql - ORM QueryBuilder asynchronous interface(1) "
"what():",
e.base().what());
}
}
{
std::future<Users> user = QueryBuilder<Users>{}
.from("users")
.selectAll()
.eq("id", "3")
.limit(1)
.single()
.order("id", false)
.execAsyncFuture(clientPtr);
try
{
const Users r = user.get();
MANDATE(r.getPrimaryKey() == 3);
}
catch (const DrogonDbException &e)
{
FAULT(
"postgresql - ORM QueryBuilder asynchronous interface(2) "
"what():",
e.base().what());
}
}
{
std::future<Row> users = QueryBuilder<Users>{}
.from("users")
.select("id")
.limit(1)
.single()
.order("id", false)
.execAsyncFuture(clientPtr);
try
{
const Row r = users.get();
MANDATE(r["id"].as<int32_t>() == 3);
}
catch (const DrogonDbException &e)
{
FAULT(
"postgresql - ORM QueryBuilder asynchronous interface(3) "
"what():",
e.base().what());
}
}
#ifdef __cpp_impl_coroutine
auto coro_test = [clientPtr, TEST_CTX]() -> drogon::Task<> {
/// 7 Test coroutines.
@ -1422,6 +1569,153 @@ DROGON_TEST(MySQLTest)
FAULT("mysql - ORM mapper synchronous interface(1) what():",
e.base().what());
}
/// Test ORM QueryBuilder
/// execSync
try
{
const std::vector<Users> users =
QueryBuilder<Users>{}.from("users").selectAll().execSync(clientPtr);
MANDATE(users.size() == 2);
}
catch (const DrogonDbException &e)
{
FAULT("mysql - ORM QueryBuilder synchronous interface(0) what():",
e.base().what());
}
try
{
const Result users =
QueryBuilder<Users>{}.from("users").select("id").execSync(
clientPtr);
MANDATE(users.size() == 2);
for (const Row &u : users)
{
MANDATE(!u["id"].isNull());
}
}
catch (const DrogonDbException &e)
{
FAULT("mysql - ORM QueryBuilder synchronous interface(1) what():",
e.base().what());
}
try
{
const Users user = QueryBuilder<Users>{}
.from("users")
.selectAll()
.eq("id", "2")
.limit(1)
.single()
.order("id", false)
.execSync(clientPtr);
MANDATE(user.getPrimaryKey() == 2);
}
catch (const DrogonDbException &e)
{
FAULT("mysql - ORM QueryBuilder synchronous interface(2) what():",
e.base().what());
}
try
{
const Row user = QueryBuilder<Users>{}
.from("users")
.select("id")
.limit(1)
.single()
.order("id", false)
.execSync(clientPtr);
MANDATE(user["id"].as<int32_t>() == 2);
}
catch (const DrogonDbException &e)
{
FAULT("mysql - ORM QueryBuilder synchronous interface(3) what():",
e.base().what());
}
/// execAsyncFuture
{
std::future<std::vector<Users>> users =
QueryBuilder<Users>{}.from("users").selectAll().execAsyncFuture(
clientPtr);
try
{
const std::vector<Users> r = users.get();
MANDATE(r.size() == 2);
}
catch (const DrogonDbException &e)
{
FAULT(
"mysql - ORM QueryBuilder asynchronous interface(0) "
"what():",
e.base().what());
}
}
{
std::future<Result> users =
QueryBuilder<Users>{}.from("users").select("id").execAsyncFuture(
clientPtr);
try
{
const Result r = users.get();
MANDATE(r.size() == 2);
for (const Row &u : r)
{
MANDATE(!u["id"].isNull());
}
}
catch (const DrogonDbException &e)
{
FAULT(
"mysql - ORM QueryBuilder asynchronous interface(1) "
"what():",
e.base().what());
}
}
{
std::future<Users> user = QueryBuilder<Users>{}
.from("users")
.selectAll()
.eq("id", "2")
.limit(1)
.single()
.order("id", false)
.execAsyncFuture(clientPtr);
try
{
const Users r = user.get();
MANDATE(r.getPrimaryKey() == 2);
}
catch (const DrogonDbException &e)
{
FAULT(
"mysql - ORM QueryBuilder asynchronous interface(2) "
"what():",
e.base().what());
}
}
{
std::future<Row> users = QueryBuilder<Users>{}
.from("users")
.select("id")
.limit(1)
.single()
.order("id", false)
.execAsyncFuture(clientPtr);
try
{
const Row r = users.get();
MANDATE(r["id"].as<int32_t>() == 2);
}
catch (const DrogonDbException &e)
{
FAULT(
"mysql - ORM QueryBuilder asynchronous interface(3) "
"what():",
e.base().what());
}
}
#ifdef __cpp_impl_coroutine
auto coro_test = [clientPtr, TEST_CTX]() -> drogon::Task<> {
/// 7 Test coroutines.
@ -2076,6 +2370,153 @@ DROGON_TEST(SQLite3Test)
FAULT("sqlite3 - ORM mapper synchronous interface(0) what():",
e.base().what());
}
/// Test ORM QueryBuilder
/// execSync
try
{
const std::vector<Users> users =
QueryBuilder<Users>{}.from("users").selectAll().execSync(clientPtr);
MANDATE(users.size() == 2);
}
catch (const DrogonDbException &e)
{
FAULT("sqlite3 - ORM QueryBuilder synchronous interface(0) what():",
e.base().what());
}
try
{
const Result users =
QueryBuilder<Users>{}.from("users").select("id").execSync(
clientPtr);
MANDATE(users.size() == 2);
for (const Row &u : users)
{
MANDATE(!u["id"].isNull());
}
}
catch (const DrogonDbException &e)
{
FAULT("sqlite3 - ORM QueryBuilder synchronous interface(1) what():",
e.base().what());
}
try
{
const Users user = QueryBuilder<Users>{}
.from("users")
.selectAll()
.eq("id", "2")
.limit(1)
.single()
.order("id", false)
.execSync(clientPtr);
MANDATE(user.getPrimaryKey() == 2);
}
catch (const DrogonDbException &e)
{
FAULT("sqlite3 - ORM QueryBuilder synchronous interface(2) what():",
e.base().what());
}
try
{
const Row user = QueryBuilder<Users>{}
.from("users")
.select("id")
.limit(1)
.single()
.order("id", false)
.execSync(clientPtr);
MANDATE(user["id"].as<int32_t>() == 2);
}
catch (const DrogonDbException &e)
{
FAULT("sqlite3 - ORM QueryBuilder synchronous interface(3) what():",
e.base().what());
}
/// execAsyncFuture
{
std::future<std::vector<Users>> users =
QueryBuilder<Users>{}.from("users").selectAll().execAsyncFuture(
clientPtr);
try
{
const std::vector<Users> r = users.get();
MANDATE(r.size() == 2);
}
catch (const DrogonDbException &e)
{
FAULT(
"sqlite3 - ORM QueryBuilder asynchronous interface(0) "
"what():",
e.base().what());
}
}
{
std::future<Result> users =
QueryBuilder<Users>{}.from("users").select("id").execAsyncFuture(
clientPtr);
try
{
const Result r = users.get();
MANDATE(r.size() == 2);
for (const Row &u : r)
{
MANDATE(!u["id"].isNull());
}
}
catch (const DrogonDbException &e)
{
FAULT(
"sqlite3 - ORM QueryBuilder asynchronous interface(1) "
"what():",
e.base().what());
}
}
{
std::future<Users> user = QueryBuilder<Users>{}
.from("users")
.selectAll()
.eq("id", "2")
.limit(1)
.single()
.order("id", false)
.execAsyncFuture(clientPtr);
try
{
const Users r = user.get();
MANDATE(r.getPrimaryKey() == 2);
}
catch (const DrogonDbException &e)
{
FAULT(
"sqlite3 - ORM QueryBuilder asynchronous interface(2) "
"what():",
e.base().what());
}
}
{
std::future<Row> users = QueryBuilder<Users>{}
.from("users")
.select("id")
.limit(1)
.single()
.order("id", false)
.execAsyncFuture(clientPtr);
try
{
const Row r = users.get();
MANDATE(r["id"].as<int32_t>() == 2);
}
catch (const DrogonDbException &e)
{
FAULT(
"sqlite3 - ORM QueryBuilder asynchronous interface(3) "
"what():",
e.base().what());
}
}
#ifdef __cpp_impl_coroutine
auto coro_test = [clientPtr, TEST_CTX]() -> drogon::Task<> {
/// 7 Test coroutines.

View File

@ -11,6 +11,7 @@
#include <drogon/orm/Field.h>
#include <drogon/orm/SqlBinder.h>
#include <drogon/orm/Mapper.h>
#include <drogon/orm/BaseBuilder.h>
#ifdef __cpp_impl_coroutine
#include <drogon/orm/CoroMapper.h>
#endif
@ -234,6 +235,10 @@ class Users
/// Relationship interfaces
private:
friend Mapper<Users>;
friend BaseBuilder<Users, true, true>;
friend BaseBuilder<Users, true, false>;
friend BaseBuilder<Users, false, true>;
friend BaseBuilder<Users, false, false>;
#ifdef __cpp_impl_coroutine
friend CoroMapper<Users>;
#endif

View File

@ -11,6 +11,7 @@
#include <drogon/orm/Field.h>
#include <drogon/orm/SqlBinder.h>
#include <drogon/orm/Mapper.h>
#include <drogon/orm/BaseBuilder.h>
#ifdef __cpp_impl_coroutine
#include <drogon/orm/CoroMapper.h>
#endif
@ -234,6 +235,10 @@ class Users
/// Relationship interfaces
private:
friend Mapper<Users>;
friend BaseBuilder<Users, true, true>;
friend BaseBuilder<Users, true, false>;
friend BaseBuilder<Users, false, true>;
friend BaseBuilder<Users, false, false>;
#ifdef __cpp_impl_coroutine
friend CoroMapper<Users>;
#endif

View File

@ -11,6 +11,7 @@
#include <drogon/orm/Field.h>
#include <drogon/orm/SqlBinder.h>
#include <drogon/orm/Mapper.h>
#include <drogon/orm/BaseBuilder.h>
#ifdef __cpp_impl_coroutine
#include <drogon/orm/CoroMapper.h>
#endif
@ -239,6 +240,10 @@ class Users
/// Relationship interfaces
private:
friend drogon::orm::Mapper<Users>;
friend drogon::orm::BaseBuilder<Users, true, true>;
friend drogon::orm::BaseBuilder<Users, true, false>;
friend drogon::orm::BaseBuilder<Users, false, true>;
friend drogon::orm::BaseBuilder<Users, false, false>;
#ifdef __cpp_impl_coroutine
friend drogon::orm::CoroMapper<Users>;
#endif