Implement QueryBuilder (#1331)
This commit is contained in:
parent
7fce0f1ff5
commit
75b106599d
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue