364 lines
10 KiB
C
364 lines
10 KiB
C
|
/**
|
||
|
*
|
||
|
* @file Criteria.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/exports.h>
|
||
|
#include <drogon/orm/SqlBinder.h>
|
||
|
#include <drogon/utils/apply.h>
|
||
|
#include <assert.h>
|
||
|
#include <memory>
|
||
|
#include <string>
|
||
|
#include <tuple>
|
||
|
#include <type_traits>
|
||
|
|
||
|
namespace Json
|
||
|
{
|
||
|
class Value;
|
||
|
}
|
||
|
namespace drogon
|
||
|
{
|
||
|
namespace orm
|
||
|
{
|
||
|
enum class CompareOperator
|
||
|
{
|
||
|
EQ,
|
||
|
NE,
|
||
|
GT,
|
||
|
GE,
|
||
|
LT,
|
||
|
LE,
|
||
|
Like,
|
||
|
NotLike,
|
||
|
In,
|
||
|
NotIn,
|
||
|
IsNull,
|
||
|
IsNotNull
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @brief Wrapper for custom SQL string
|
||
|
*/
|
||
|
struct CustomSql
|
||
|
{
|
||
|
explicit CustomSql(std::string content) : content_(std::move(content))
|
||
|
{
|
||
|
}
|
||
|
std::string content_;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @brief User-defined literal to convert a string to CustomSql
|
||
|
*/
|
||
|
inline CustomSql operator""_sql(const char *str, size_t)
|
||
|
{
|
||
|
return CustomSql(str);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief this class represents a comparison condition.
|
||
|
*/
|
||
|
class DROGON_EXPORT Criteria
|
||
|
{
|
||
|
public:
|
||
|
/**
|
||
|
* @brief Check if the condition is empty.
|
||
|
*
|
||
|
* @return true means the condition is not empty.
|
||
|
* @return false means the condition is empty.
|
||
|
*/
|
||
|
explicit operator bool() const
|
||
|
{
|
||
|
return !conditionString_.empty();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief return the condition string in SQL.
|
||
|
*
|
||
|
* @return std::string
|
||
|
*/
|
||
|
std::string criteriaString() const
|
||
|
{
|
||
|
return conditionString_;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Construct a new custom Criteria object
|
||
|
*
|
||
|
* @param sql The SQL statement to be executed.
|
||
|
* @param args The parameters to be passed to the placeholders.
|
||
|
*
|
||
|
* @note
|
||
|
*
|
||
|
* Use $? as placeholders in the SQL statement.
|
||
|
*
|
||
|
* Be careful that the placeholders are not the same as the ones in
|
||
|
* functions such as drogon::orm::DbClient::execSqlAsync. Which means you
|
||
|
* couldn't use numeric placeholders such as $1, $2 to represent the order
|
||
|
* of the arguments.
|
||
|
*
|
||
|
* The arguments should be in the same order as the placeholders.
|
||
|
*
|
||
|
*/
|
||
|
template <typename... Arguments>
|
||
|
explicit Criteria(const CustomSql &sql, Arguments &&...args)
|
||
|
{
|
||
|
conditionString_ = sql.content_;
|
||
|
outputArgumentsFunc_ =
|
||
|
[args = std::make_tuple(std::forward<Arguments>(args)...)](
|
||
|
internal::SqlBinder &binder) mutable {
|
||
|
return apply(
|
||
|
[&binder](auto &&...args) {
|
||
|
(void)std::initializer_list<int>{
|
||
|
(binder << std::forward<Arguments>(args), 0)...};
|
||
|
},
|
||
|
std::move(args));
|
||
|
};
|
||
|
}
|
||
|
|
||
|
template <typename... Arguments>
|
||
|
explicit Criteria(CustomSql &&sql, Arguments &&...args)
|
||
|
{
|
||
|
conditionString_ = std::move(sql.content_);
|
||
|
outputArgumentsFunc_ =
|
||
|
[args = std::make_tuple(std::forward<Arguments>(args)...)](
|
||
|
internal::SqlBinder &binder) mutable {
|
||
|
return apply(
|
||
|
[&binder](auto &&...args) {
|
||
|
(void)std::initializer_list<int>{
|
||
|
(binder << std::forward<Arguments>(args), 0)...};
|
||
|
},
|
||
|
std::move(args));
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Construct a new Criteria object
|
||
|
*
|
||
|
* @tparam T the type of the object to be compared with.
|
||
|
* @param colName The name of the column.
|
||
|
* @param opera The type of the comparison.
|
||
|
* @param arg The object to be compared with.
|
||
|
*/
|
||
|
template <typename T>
|
||
|
Criteria(const std::string &colName, const CompareOperator &opera, T &&arg)
|
||
|
{
|
||
|
assert(opera != CompareOperator::IsNotNull &&
|
||
|
opera != CompareOperator::IsNull);
|
||
|
conditionString_ = colName;
|
||
|
switch (opera)
|
||
|
{
|
||
|
case CompareOperator::EQ:
|
||
|
conditionString_ += " = $?";
|
||
|
break;
|
||
|
case CompareOperator::NE:
|
||
|
conditionString_ += " != $?";
|
||
|
break;
|
||
|
case CompareOperator::GT:
|
||
|
conditionString_ += " > $?";
|
||
|
break;
|
||
|
case CompareOperator::GE:
|
||
|
conditionString_ += " >= $?";
|
||
|
break;
|
||
|
case CompareOperator::LT:
|
||
|
conditionString_ += " < $?";
|
||
|
break;
|
||
|
case CompareOperator::LE:
|
||
|
conditionString_ += " <= $?";
|
||
|
break;
|
||
|
case CompareOperator::Like:
|
||
|
conditionString_ += " like $?";
|
||
|
break;
|
||
|
case CompareOperator::NotLike:
|
||
|
conditionString_ += " not like $?";
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
outputArgumentsFunc_ =
|
||
|
[arg = std::forward<T>(arg)](internal::SqlBinder &binder) {
|
||
|
binder << arg;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
Criteria(const std::string &colName,
|
||
|
const CompareOperator &opera,
|
||
|
const std::vector<T> &args)
|
||
|
{
|
||
|
const auto argsSize = args.size();
|
||
|
assert(((opera == CompareOperator::In) ||
|
||
|
(opera == CompareOperator::NotIn)) &&
|
||
|
(argsSize > 0));
|
||
|
if (opera == CompareOperator::In)
|
||
|
{
|
||
|
conditionString_ = colName + " in (";
|
||
|
}
|
||
|
else if (opera == CompareOperator::NotIn)
|
||
|
{
|
||
|
conditionString_ = colName + " not in (";
|
||
|
}
|
||
|
for (size_t i = 0; i < argsSize; ++i)
|
||
|
{
|
||
|
if (i < (argsSize - 1))
|
||
|
conditionString_.append("$?,");
|
||
|
else
|
||
|
conditionString_.append("$?");
|
||
|
}
|
||
|
conditionString_.append(")");
|
||
|
outputArgumentsFunc_ = [args](internal::SqlBinder &binder) {
|
||
|
for (auto &arg : args)
|
||
|
{
|
||
|
binder << arg;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
Criteria(const std::string &colName,
|
||
|
const CompareOperator &opera,
|
||
|
std::vector<T> &&args)
|
||
|
{
|
||
|
const auto argsSize = args.size();
|
||
|
assert(((opera == CompareOperator::In) ||
|
||
|
(opera == CompareOperator::NotIn)) &&
|
||
|
(argsSize > 0));
|
||
|
if (opera == CompareOperator::In)
|
||
|
{
|
||
|
conditionString_ = colName + " in (";
|
||
|
}
|
||
|
else if (opera == CompareOperator::NotIn)
|
||
|
{
|
||
|
conditionString_ = colName + " not in (";
|
||
|
}
|
||
|
for (size_t i = 0; i < argsSize; ++i)
|
||
|
{
|
||
|
if (i < (argsSize - 1))
|
||
|
conditionString_.append("$?,");
|
||
|
else
|
||
|
conditionString_.append("$?");
|
||
|
}
|
||
|
conditionString_.append(")");
|
||
|
outputArgumentsFunc_ =
|
||
|
[args = std::move(args)](internal::SqlBinder &binder) {
|
||
|
for (auto &arg : args)
|
||
|
{
|
||
|
binder << arg;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
Criteria(const std::string &colName,
|
||
|
const CompareOperator &opera,
|
||
|
std::vector<T> &args)
|
||
|
: Criteria(colName, opera, (const std::vector<T> &)args)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Construct a new Criteria object presenting a equal expression
|
||
|
*
|
||
|
* @tparam T The type of the object to be equal to.
|
||
|
* @param colName The name of the column.
|
||
|
* @param arg The object to be equal to.
|
||
|
*/
|
||
|
template <typename T>
|
||
|
Criteria(const std::string &colName, T &&arg)
|
||
|
: Criteria(colName, CompareOperator::EQ, std::forward<T>(arg))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Construct a new Criteria object presenting a expression without
|
||
|
* parameters.
|
||
|
*
|
||
|
* @param colName The name of the column.
|
||
|
* @param opera The type of the comparison. it must be one of the following:
|
||
|
* @code
|
||
|
CompareOperator::IsNotNull
|
||
|
CompareOperator::IsNull
|
||
|
@endcode
|
||
|
*/
|
||
|
Criteria(const std::string &colName, const CompareOperator &opera)
|
||
|
{
|
||
|
assert(opera == CompareOperator::IsNotNull ||
|
||
|
opera == CompareOperator::IsNull);
|
||
|
conditionString_ = colName;
|
||
|
switch (opera)
|
||
|
{
|
||
|
case CompareOperator::IsNull:
|
||
|
conditionString_ += " is null";
|
||
|
break;
|
||
|
case CompareOperator::IsNotNull:
|
||
|
conditionString_ += " is not null";
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
Criteria(const std::string &colName, CompareOperator &opera)
|
||
|
: Criteria(colName, (const CompareOperator &)opera)
|
||
|
{
|
||
|
}
|
||
|
Criteria(const std::string &colName, CompareOperator &&opera)
|
||
|
: Criteria(colName, (const CompareOperator &)opera)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Construct a new Criteria object
|
||
|
*
|
||
|
* @param json A json object representing the criteria
|
||
|
* @note the json object must be a array of three items, the first is the
|
||
|
* name of the field, the second is the comparison operator and the third is
|
||
|
* the value to be compared.
|
||
|
* The valid operators are "=","<",">","<=",">=","!=","in"
|
||
|
* for example:
|
||
|
* ["id","=",1] means 'id = 1'
|
||
|
* ["id","!=",null] means 'id is not null'
|
||
|
* ["user_name","in",["Tom","Bob"]] means 'user_name in ('Tom', 'Bob')'
|
||
|
* ["price","<",1000] means 'price < 1000'
|
||
|
*/
|
||
|
explicit Criteria(const Json::Value &json) noexcept(false);
|
||
|
Criteria() = default;
|
||
|
|
||
|
/**
|
||
|
* @brief Output arguments to the SQL binder object.
|
||
|
*
|
||
|
* @param binder The SQL binder object.
|
||
|
* @note This method must be called by drogon.
|
||
|
*/
|
||
|
void outputArgs(internal::SqlBinder &binder) const
|
||
|
{
|
||
|
if (outputArgumentsFunc_)
|
||
|
outputArgumentsFunc_(binder);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
friend DROGON_EXPORT const Criteria operator&&(Criteria cond1,
|
||
|
Criteria cond2);
|
||
|
|
||
|
friend DROGON_EXPORT const Criteria operator||(Criteria cond1,
|
||
|
Criteria cond2);
|
||
|
std::string conditionString_;
|
||
|
std::function<void(internal::SqlBinder &)> outputArgumentsFunc_;
|
||
|
}; // namespace orm
|
||
|
|
||
|
DROGON_EXPORT const Criteria operator&&(Criteria cond1, Criteria cond2);
|
||
|
DROGON_EXPORT const Criteria operator||(Criteria cond1, Criteria cond2);
|
||
|
|
||
|
} // namespace orm
|
||
|
} // namespace drogon
|