mirror of
https://github.com/getml/sqlgen.git
synced 2026-02-17 06:08:39 -06:00
Added type checks (#15)
This commit is contained in:
committed by
GitHub
parent
3d8c4ecbf1
commit
50800fdf0c
@@ -182,6 +182,10 @@ sqlgen provides comprehensive compile-time checks and runtime protection:
|
||||
const auto query = read<std::vector<Person>> |
|
||||
where("color"_c == "blue");
|
||||
|
||||
// Compile-time error: Cannot compare column "age" to a string
|
||||
const auto query = read<std::vector<Person>> |
|
||||
where("age"_c == "Homer");
|
||||
|
||||
// Runtime protection against SQL injection
|
||||
std::vector<Person> get_people(const auto& conn,
|
||||
const sqlgen::AlphaNumeric& first_name) {
|
||||
|
||||
@@ -86,7 +86,10 @@ CREATE TABLE IF NOT EXISTS "my_schema"."People"(
|
||||
As we have seen, all columns are non-nullable by default. But what if we do want
|
||||
nullability?
|
||||
|
||||
As with reflect-cpp, `std::optional`, `std::shared_ptr` and `std::unique_ptr` are interpreted as nullable types. So you can create nullable fields as follows:
|
||||
As with reflect-cpp, `std::optional`, `std::shared_ptr` and `std::unique_ptr` are interpreted as nullable types,
|
||||
but it is strongly recommended that you use `std::optional`.
|
||||
|
||||
So you can create nullable fields as follows:
|
||||
|
||||
```cpp
|
||||
struct People {
|
||||
@@ -100,7 +103,7 @@ struct People {
|
||||
|
||||
- All fields are non-nullable by default
|
||||
- Nullable fields can be defined using:
|
||||
- `std::optional<T>`
|
||||
- `std::optional<T>` (strongly recommended)
|
||||
- `std::shared_ptr<T>`
|
||||
- `std::unique_ptr<T>`
|
||||
- Use `std::nullopt` or `nullptr` to represent NULL values
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "transpilation/Col.hpp"
|
||||
#include "transpilation/Condition.hpp"
|
||||
#include "transpilation/Desc.hpp"
|
||||
#include "transpilation/Set.hpp"
|
||||
@@ -14,10 +15,13 @@ namespace sqlgen {
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
struct Col {
|
||||
using ColType = transpilation::Col<_name>;
|
||||
using Name = rfl::Literal<_name>;
|
||||
|
||||
/// Signals to order_by that this column is to be sorted in descending order.
|
||||
auto desc() const noexcept { return transpilation::Desc<Col<_name>>{}; }
|
||||
auto desc() const noexcept {
|
||||
return transpilation::Desc<transpilation::Col<_name>>{};
|
||||
}
|
||||
|
||||
/// Returns the column name.
|
||||
std::string name() const noexcept { return Name().str(); }
|
||||
@@ -25,36 +29,46 @@ struct Col {
|
||||
/// Returns an IS NULL condition.
|
||||
auto is_null() const noexcept {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::is_null(*this));
|
||||
transpilation::conditions::is_null(transpilation::Col<_name>{}));
|
||||
}
|
||||
|
||||
/// Returns a IS NOT NULL condition.
|
||||
auto is_not_null() const noexcept {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::is_not_null(*this));
|
||||
transpilation::conditions::is_not_null(transpilation::Col<_name>{}));
|
||||
}
|
||||
|
||||
/// Returns a LIKE condition.
|
||||
auto like(const std::string& _pattern) const noexcept {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::like(*this, _pattern));
|
||||
transpilation::conditions::like(transpilation::Col<_name>{}, _pattern));
|
||||
}
|
||||
|
||||
/// Returns a NOT LIKE condition.
|
||||
auto not_like(const std::string& _pattern) const noexcept {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::not_like(*this, _pattern));
|
||||
return transpilation::make_condition(transpilation::conditions::not_like(
|
||||
transpilation::Col<_name>{}, _pattern));
|
||||
}
|
||||
|
||||
/// Returns a SET clause in an UPDATE statement.
|
||||
template <class T>
|
||||
auto set(const T& _to) const noexcept {
|
||||
return transpilation::Set<Col<_name>, std::remove_cvref_t<T>>{.to = _to};
|
||||
return transpilation::Set<transpilation::Col<_name>,
|
||||
std::remove_cvref_t<T>>{.to = _to};
|
||||
}
|
||||
|
||||
/// Returns a SET clause in an UPDATE statement.
|
||||
template <rfl::internal::StringLiteral _other_name>
|
||||
auto set(const Col<_other_name>& _to) const noexcept {
|
||||
return transpilation::Set<transpilation::Col<_name>,
|
||||
transpilation::Col<_other_name>>{
|
||||
.to = transpilation::Col<_other_name>{}};
|
||||
}
|
||||
|
||||
/// Returns a SET clause in an UPDATE statement.
|
||||
auto set(const char* _to) const noexcept {
|
||||
return transpilation::Set<Col<_name>, std::string>{.to = _to};
|
||||
return transpilation::Set<transpilation::Col<_name>, std::string>{.to =
|
||||
_to};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -69,79 +83,79 @@ auto operator"" _c() {
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator==(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::equal(Col<_name1>{}, Col<_name2>{}));
|
||||
return transpilation::make_condition(transpilation::conditions::equal(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator==(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::equal(
|
||||
Col<_name1>{}, transpilation::make_value(_t)));
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator!=(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::not_equal(Col<_name1>{}, Col<_name2>{}));
|
||||
return transpilation::make_condition(transpilation::conditions::not_equal(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator!=(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::not_equal(
|
||||
Col<_name1>{}, transpilation::make_value(_t)));
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator<(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::lesser_than(Col<_name1>{}, Col<_name2>{}));
|
||||
return transpilation::make_condition(transpilation::conditions::lesser_than(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator<(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::lesser_than(
|
||||
Col<_name1>{}, transpilation::make_value(_t)));
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator<=(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::lesser_equal(Col<_name1>{}, Col<_name2>{}));
|
||||
return transpilation::make_condition(transpilation::conditions::lesser_equal(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator<=(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::lesser_equal(
|
||||
Col<_name1>{}, transpilation::make_value(_t)));
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator>(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::greater_than(Col<_name1>{}, Col<_name2>{}));
|
||||
return transpilation::make_condition(transpilation::conditions::greater_than(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator>(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::greater_than(
|
||||
Col<_name1>{}, transpilation::make_value(_t)));
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
auto operator>=(const Col<_name1>&, const Col<_name2>&) {
|
||||
return transpilation::make_condition(
|
||||
transpilation::conditions::greater_equal(Col<_name1>{}, Col<_name2>{}));
|
||||
return transpilation::make_condition(transpilation::conditions::greater_equal(
|
||||
transpilation::Col<_name1>{}, transpilation::Col<_name2>{}));
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name1, class T>
|
||||
auto operator>=(const Col<_name1>&, const T& _t) {
|
||||
return transpilation::make_condition(transpilation::conditions::greater_equal(
|
||||
Col<_name1>{}, transpilation::make_value(_t)));
|
||||
transpilation::Col<_name1>{}, transpilation::make_value(_t)));
|
||||
}
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
@@ -44,8 +44,9 @@ template <rfl::internal::StringLiteral _name, class ValueType, class WhereType,
|
||||
struct CreateIndex {
|
||||
auto operator()(const auto& _conn) const {
|
||||
return create_index_impl<
|
||||
ValueType, transpilation::columns_t<ValueType, ColTypes...>, WhereType>(
|
||||
_conn, _name.str(), unique_, if_not_exists_, where_);
|
||||
ValueType,
|
||||
transpilation::columns_t<ValueType, typename ColTypes::ColType...>,
|
||||
WhereType>(_conn, _name.str(), unique_, if_not_exists_, where_);
|
||||
}
|
||||
|
||||
bool unique_ = false;
|
||||
|
||||
@@ -25,8 +25,9 @@ auto operator|(const Read<ContainerType, WhereType, OrderByType, LimitType>& _r,
|
||||
static_assert(sizeof...(ColTypes) != 0,
|
||||
"You must assign at least one column to order by.");
|
||||
return Read<ContainerType, WhereType,
|
||||
transpilation::order_by_t<transpilation::value_t<ContainerType>,
|
||||
std::remove_cvref_t<ColTypes>...>,
|
||||
transpilation::order_by_t<
|
||||
transpilation::value_t<ContainerType>,
|
||||
typename std::remove_cvref_t<ColTypes>::ColType...>,
|
||||
LimitType>{.where_ = _r.where_};
|
||||
}
|
||||
|
||||
|
||||
20
include/sqlgen/transpilation/Col.hpp
Normal file
20
include/sqlgen/transpilation/Col.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_COL_HPP_
|
||||
#define SQLGEN_TRANSPILATION_COL_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
struct Col {
|
||||
using ColType = Col<_name>;
|
||||
using Name = rfl::Literal<_name>;
|
||||
|
||||
/// Returns the column name.
|
||||
std::string name() const noexcept { return Name().str(); }
|
||||
};
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class _ColType>
|
||||
template <class _C>
|
||||
struct Desc {
|
||||
using ColType = _ColType;
|
||||
using ColType = Desc<_C>;
|
||||
};
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace sqlgen::transpilation {
|
||||
/// Defines the SET clause in an UPDATE statement.
|
||||
template <class _ColType, class T>
|
||||
struct Set {
|
||||
using ColType = _ColType;
|
||||
using ColType = typename _ColType::ColType;
|
||||
T to;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Literal.hpp"
|
||||
#include "../col.hpp"
|
||||
#include "Col.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
@@ -14,7 +14,7 @@ struct ColumnExists;
|
||||
|
||||
template <rfl::internal::StringLiteral _col_name,
|
||||
rfl::internal::StringLiteral... _field_names>
|
||||
struct ColumnExists<Literal<_field_names...>, Col<_col_name>> {
|
||||
struct ColumnExists<Literal<_field_names...>, transpilation::Col<_col_name>> {
|
||||
static constexpr bool value = (false || ... || (_col_name == _field_names));
|
||||
static_assert(value, "Column does not exist.");
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "../col.hpp"
|
||||
#include "Desc.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
|
||||
|
||||
@@ -5,12 +5,16 @@ namespace sqlgen::transpilation::conditions {
|
||||
|
||||
template <class CondType1, class CondType2>
|
||||
struct And {
|
||||
using ResultType = bool;
|
||||
|
||||
CondType1 cond1;
|
||||
CondType2 cond2;
|
||||
};
|
||||
|
||||
template <class OpType1, class OpType2>
|
||||
struct Equal {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType1 op1;
|
||||
OpType2 op2;
|
||||
};
|
||||
@@ -23,6 +27,8 @@ auto equal(const OpType1& _op1, const OpType2& _op2) {
|
||||
|
||||
template <class OpType1, class OpType2>
|
||||
struct GreaterEqual {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType1 op1;
|
||||
OpType2 op2;
|
||||
};
|
||||
@@ -35,6 +41,8 @@ auto greater_equal(const OpType1& _op1, const OpType2& _op2) {
|
||||
|
||||
template <class OpType1, class OpType2>
|
||||
struct GreaterThan {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType1 op1;
|
||||
OpType2 op2;
|
||||
};
|
||||
@@ -47,6 +55,8 @@ auto greater_than(const OpType1& _op1, const OpType2& _op2) {
|
||||
|
||||
template <class OpType>
|
||||
struct IsNull {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType op;
|
||||
};
|
||||
|
||||
@@ -57,6 +67,8 @@ auto is_null(const OpType& _op) {
|
||||
|
||||
template <class OpType>
|
||||
struct IsNotNull {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType op;
|
||||
};
|
||||
|
||||
@@ -67,6 +79,8 @@ auto is_not_null(const OpType& _op) {
|
||||
|
||||
template <class OpType1, class OpType2>
|
||||
struct LesserEqual {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType1 op1;
|
||||
OpType2 op2;
|
||||
};
|
||||
@@ -79,6 +93,8 @@ auto lesser_equal(const OpType1& _op1, const OpType2& _op2) {
|
||||
|
||||
template <class OpType1, class OpType2>
|
||||
struct LesserThan {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType1 op1;
|
||||
OpType2 op2;
|
||||
};
|
||||
@@ -91,6 +107,8 @@ auto lesser_than(const OpType1& _op1, const OpType2& _op2) {
|
||||
|
||||
template <class OpType>
|
||||
struct Like {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType op;
|
||||
std::string pattern;
|
||||
};
|
||||
@@ -102,6 +120,8 @@ auto like(const OpType& _op, const std::string& _pattern) {
|
||||
|
||||
template <class OpType1, class OpType2>
|
||||
struct NotEqual {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType1 op1;
|
||||
OpType2 op2;
|
||||
};
|
||||
@@ -114,6 +134,8 @@ auto not_equal(const OpType1& _op1, const OpType2& _op2) {
|
||||
|
||||
template <class OpType>
|
||||
struct NotLike {
|
||||
using ResultType = bool;
|
||||
|
||||
OpType op;
|
||||
std::string pattern;
|
||||
};
|
||||
@@ -125,6 +147,8 @@ auto not_like(const OpType& _op, const std::string& _pattern) {
|
||||
|
||||
template <class CondType1, class CondType2>
|
||||
struct Or {
|
||||
using ResultType = bool;
|
||||
|
||||
CondType1 cond1;
|
||||
CondType2 cond2;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "../col.hpp"
|
||||
#include "Col.hpp"
|
||||
#include "Desc.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
|
||||
@@ -15,14 +15,14 @@ template <class _ColType>
|
||||
struct OrderByWrapper;
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
struct OrderByWrapper<Col<_name>> {
|
||||
using ColType = Col<_name>;
|
||||
struct OrderByWrapper<transpilation::Col<_name>> {
|
||||
using ColType = transpilation::Col<_name>;
|
||||
constexpr static bool desc = false;
|
||||
};
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
struct OrderByWrapper<Desc<Col<_name>>> {
|
||||
using ColType = Col<_name>;
|
||||
struct OrderByWrapper<Desc<transpilation::Col<_name>>> {
|
||||
using ColType = transpilation::Col<_name>;
|
||||
constexpr static bool desc = true;
|
||||
};
|
||||
|
||||
@@ -38,8 +38,8 @@ auto make_order_by() {
|
||||
}
|
||||
|
||||
template <class T, class... ColTypes>
|
||||
using order_by_t =
|
||||
std::invoke_result_t<decltype(make_order_by<T, ColTypes...>)>;
|
||||
using order_by_t = std::invoke_result_t<
|
||||
decltype(make_order_by<T, typename ColTypes::ColType...>)>;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
|
||||
28
include/sqlgen/transpilation/remove_reflection_t.hpp
Normal file
28
include/sqlgen/transpilation/remove_reflection_t.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_REMOVEREFLECTIONT_HPP_
|
||||
#define SQLGEN_TRANSPILATION_REMOVEREFLECTIONT_HPP_
|
||||
|
||||
#include "has_reflection_method.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T>
|
||||
struct RemoveReflection;
|
||||
|
||||
template <class T>
|
||||
struct RemoveReflection {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
requires has_reflection_method<std::remove_cvref_t<T>>
|
||||
struct RemoveReflection<T> {
|
||||
using Type = typename RemoveReflection<
|
||||
typename std::remove_cvref_t<T>::ReflectionType>::Type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using remove_reflection_t = typename RemoveReflection<T>::Type;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_TO_CONDITION_HPP_
|
||||
#define SQLGEN_TRANSPILATION_TO_CONDITION_HPP_
|
||||
|
||||
#include <concepts>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
@@ -12,6 +13,7 @@
|
||||
#include "all_columns_exist.hpp"
|
||||
#include "conditions.hpp"
|
||||
#include "to_value.hpp"
|
||||
#include "underlying_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
@@ -43,6 +45,9 @@ template <class T, rfl::internal::StringLiteral _name1,
|
||||
struct ToCondition<T, conditions::Equal<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
"Must be equality comparable.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::Equal{
|
||||
@@ -55,6 +60,9 @@ struct ToCondition<T, conditions::Equal<Col<_name1>, Col<_name2>>> {
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::Equal<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be equality comparable.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::Equal{
|
||||
@@ -69,6 +77,9 @@ template <class T, rfl::internal::StringLiteral _name1,
|
||||
struct ToCondition<T, conditions::GreaterEqual<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::GreaterEqual{
|
||||
@@ -81,6 +92,9 @@ struct ToCondition<T, conditions::GreaterEqual<Col<_name1>, Col<_name2>>> {
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::GreaterEqual<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::GreaterEqual{
|
||||
@@ -95,6 +109,9 @@ template <class T, rfl::internal::StringLiteral _name1,
|
||||
struct ToCondition<T, conditions::GreaterThan<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::GreaterThan{
|
||||
@@ -107,6 +124,9 @@ struct ToCondition<T, conditions::GreaterThan<Col<_name1>, Col<_name2>>> {
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::GreaterThan<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::GreaterThan{
|
||||
@@ -121,6 +141,9 @@ template <class T, rfl::internal::StringLiteral _name1,
|
||||
struct ToCondition<T, conditions::LesserEqual<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::LesserEqual{
|
||||
@@ -133,6 +156,9 @@ struct ToCondition<T, conditions::LesserEqual<Col<_name1>, Col<_name2>>> {
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::LesserEqual<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::LesserEqual{
|
||||
@@ -147,6 +173,9 @@ template <class T, rfl::internal::StringLiteral _name1,
|
||||
struct ToCondition<T, conditions::LesserThan<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::LesserThan{
|
||||
@@ -159,6 +188,9 @@ struct ToCondition<T, conditions::LesserThan<Col<_name1>, Col<_name2>>> {
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::LesserThan<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::totally_ordered_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be totally ordered.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::LesserThan{
|
||||
@@ -171,6 +203,10 @@ struct ToCondition<T, conditions::LesserThan<Col<_name>, Value<V>>> {
|
||||
template <class T, rfl::internal::StringLiteral _name>
|
||||
struct ToCondition<T, conditions::Like<Col<_name>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(
|
||||
std::equality_comparable_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<std::string>>>,
|
||||
"Must be equality comparable with a string.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::Like{
|
||||
@@ -204,6 +240,9 @@ template <class T, rfl::internal::StringLiteral _name1,
|
||||
struct ToCondition<T, conditions::NotEqual<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>, Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Col<_name1>>,
|
||||
underlying_t<T, Col<_name2>>>,
|
||||
"Must be equality comparable.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::NotEqual{
|
||||
@@ -216,6 +255,9 @@ struct ToCondition<T, conditions::NotEqual<Col<_name1>, Col<_name2>>> {
|
||||
template <class T, rfl::internal::StringLiteral _name, class V>
|
||||
struct ToCondition<T, conditions::NotEqual<Col<_name>, Value<V>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(std::equality_comparable_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<V>>>,
|
||||
"Must be equality comparable.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::NotEqual{
|
||||
@@ -228,6 +270,10 @@ struct ToCondition<T, conditions::NotEqual<Col<_name>, Value<V>>> {
|
||||
template <class T, rfl::internal::StringLiteral _name>
|
||||
struct ToCondition<T, conditions::NotLike<Col<_name>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
static_assert(
|
||||
std::equality_comparable_with<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<std::string>>>,
|
||||
"Must be equality comparable with a string.");
|
||||
|
||||
dynamic::Condition operator()(const auto& _cond) const {
|
||||
return dynamic::Condition{.val = dynamic::Condition::NotLike{
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../col.hpp"
|
||||
#include "../dynamic/Table.hpp"
|
||||
#include "../dynamic/Update.hpp"
|
||||
#include "Col.hpp"
|
||||
#include "Set.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
#include "get_schema.hpp"
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "to_condition.hpp"
|
||||
#include "to_sets.hpp"
|
||||
#include "to_value.hpp"
|
||||
#include "underlying_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
@@ -25,8 +26,12 @@ template <class T, class SetType>
|
||||
struct ToSet;
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name, class ToType>
|
||||
struct ToSet<T, Set<Col<_name>, ToType>> {
|
||||
static_assert(all_columns_exist<T, Col<_name>>(), "All columns must exist.");
|
||||
struct ToSet<T, Set<transpilation::Col<_name>, ToType>> {
|
||||
static_assert(all_columns_exist<T, transpilation::Col<_name>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(std::is_convertible_v<underlying_t<T, Col<_name>>,
|
||||
underlying_t<T, Value<ToType>>>,
|
||||
"Must be convertible.");
|
||||
|
||||
dynamic::Update::Set operator()(const auto& _set) const {
|
||||
return dynamic::Update::Set{
|
||||
@@ -38,9 +43,15 @@ struct ToSet<T, Set<Col<_name>, ToType>> {
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name1,
|
||||
rfl::internal::StringLiteral _name2>
|
||||
struct ToSet<T, Set<Col<_name1>, Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, Col<_name1>>(), "All columns must exist.");
|
||||
static_assert(all_columns_exist<T, Col<_name2>>(), "All columns must exist.");
|
||||
struct ToSet<T, Set<transpilation::Col<_name1>, transpilation::Col<_name2>>> {
|
||||
static_assert(all_columns_exist<T, transpilation::Col<_name1>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(all_columns_exist<T, transpilation::Col<_name2>>(),
|
||||
"All columns must exist.");
|
||||
static_assert(
|
||||
std::is_convertible_v<underlying_t<T, transpilation::Col<_name2>>,
|
||||
underlying_t<T, transpilation::Col<_name1>>>,
|
||||
"Must be convertible.");
|
||||
|
||||
dynamic::Update::Set operator()(const auto& _set) const {
|
||||
return dynamic::Update::Set{
|
||||
|
||||
@@ -31,9 +31,9 @@ template <rfl::internal::StringLiteral _name, class ValueType, class WhereType,
|
||||
struct ToSQL<CreateIndex<_name, ValueType, WhereType, ColTypes...>> {
|
||||
dynamic::Statement operator()(const auto& _create_index) const {
|
||||
return transpilation::to_create_index<
|
||||
ValueType, columns_t<ValueType, ColTypes...>, WhereType>(
|
||||
_name.str(), _create_index.unique_, _create_index.if_not_exists_,
|
||||
_create_index.where_);
|
||||
ValueType, columns_t<ValueType, typename ColTypes::ColType...>,
|
||||
WhereType>(_name.str(), _create_index.unique_,
|
||||
_create_index.if_not_exists_, _create_index.where_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
38
include/sqlgen/transpilation/underlying_t.hpp
Normal file
38
include/sqlgen/transpilation/underlying_t.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_UNDERLYINGT_HPP_
|
||||
#define SQLGEN_TRANSPILATION_UNDERLYINGT_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Col.hpp"
|
||||
#include "Desc.hpp"
|
||||
#include "Value.hpp"
|
||||
#include "remove_reflection_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T, class _Type>
|
||||
struct Underlying;
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name>
|
||||
struct Underlying<T, Col<_name>> {
|
||||
using Type = remove_reflection_t<rfl::field_type_t<_name, T>>;
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _name>
|
||||
struct Underlying<T, Desc<Col<_name>>> {
|
||||
using Type = remove_reflection_t<rfl::field_type_t<_name, T>>;
|
||||
};
|
||||
|
||||
template <class T, class _Type>
|
||||
struct Underlying<T, Value<_Type>> {
|
||||
using Type = _Type;
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
using underlying_t =
|
||||
typename Underlying<std::remove_cvref_t<T>, std::remove_cvref_t<U>>::Type;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
47
tests/sqlite/test_where_with_nullable.cpp
Normal file
47
tests/sqlite/test_where_with_nullable.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <rfl/json.hpp>
|
||||
#include <sqlgen.hpp>
|
||||
#include <sqlgen/sqlite.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace test_where_with_nullable {
|
||||
|
||||
struct Person {
|
||||
sqlgen::PrimaryKey<uint32_t> id;
|
||||
std::optional<std::string> first_name;
|
||||
std::string last_name;
|
||||
int age;
|
||||
};
|
||||
|
||||
TEST(sqlite, test_where_with_nullable) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
|
||||
Person{.id = 2, .last_name = "Simpson", .age = 8},
|
||||
Person{
|
||||
.id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0},
|
||||
Person{
|
||||
.id = 4, .first_name = "Hugo", .last_name = "Simpson", .age = 10}});
|
||||
|
||||
const auto conn = sqlgen::sqlite::connect();
|
||||
|
||||
sqlgen::write(conn, people1);
|
||||
|
||||
using namespace sqlgen;
|
||||
|
||||
const auto query = sqlgen::read<std::vector<Person>> |
|
||||
where("age"_c < 18 and "first_name"_c != "Hugo") |
|
||||
order_by("age"_c);
|
||||
|
||||
const auto people2 = query(conn).value();
|
||||
|
||||
const std::string expected =
|
||||
R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])";
|
||||
|
||||
EXPECT_EQ(rfl::json::write(people2), expected);
|
||||
}
|
||||
|
||||
} // namespace test_where_with_nullable
|
||||
Reference in New Issue
Block a user