Added type checks (#15)

This commit is contained in:
Dr. Patrick Urbanke (劉自成)
2025-05-29 19:45:01 +02:00
committed by GitHub
parent 3d8c4ecbf1
commit 50800fdf0c
18 changed files with 290 additions and 54 deletions

View File

@@ -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) {

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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_};
}

View 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

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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.");
};

View File

@@ -5,7 +5,6 @@
#include <type_traits>
#include <vector>
#include "../col.hpp"
#include "Desc.hpp"
#include "all_columns_exist.hpp"

View File

@@ -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;
};

View File

@@ -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

View 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

View File

@@ -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{

View File

@@ -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{

View File

@@ -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_);
}
};

View 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

View 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