Added the _c operator

This commit is contained in:
Dr. Patrick Urbanke
2025-05-06 05:13:06 +02:00
parent cfb4f4e0ef
commit b02dfe4f80
12 changed files with 188 additions and 30 deletions
+11 -2
View File
@@ -49,8 +49,17 @@ and print the results as a JSON:
const auto conn = sqlgen::postgres::connect(credentials);
const sqlgen::Result<std::vector<People>> result =
sqlgen::read<std::vector<People>>(conn);
using namespace sqlgen;
// Query that returns the 100 youngest children.
const auto get_children = sqlgen::read<std::vector<Person>> |
where("age"_c < 18) |
order_by("age"_c) |
limit(100);
// Actually executes the query.
// Returns sqlgen::Result<std::vector<People>>
const auto result = get_children(conn);
if (result) {
std::cout << rfl::json::write(*result) << std::endl;
+5
View File
@@ -25,6 +25,11 @@ struct Col {
template <rfl::internal::StringLiteral _name>
const auto col = Col<_name>{};
template <rfl::internal::StringLiteral _name>
auto operator"" _c() {
return Col<_name>{};
}
template <rfl::internal::StringLiteral _name1,
rfl::internal::StringLiteral _name2>
auto operator==(const Col<_name1>&, const Col<_name2>&) {
+1 -1
View File
@@ -21,7 +21,7 @@ auto operator|(const Read<ContainerType, WhereType, OrderByType, LimitType>& _r,
"You cannot call order_by twice (but you can order by more "
"than one column).");
static_assert(std::is_same_v<LimitType, Nothing>,
"Limit must be called after order_by.");
"You cannot call limit(...) must be called before order_by.");
static_assert(sizeof...(ColTypes) != 0,
"You must assign at least one column to order by.");
return Read<ContainerType, WhereType,
+55
View File
@@ -56,9 +56,19 @@ class Connection : public sqlgen::Connection {
std::string add_not_null_if_necessary(
const dynamic::types::Properties& _p) const noexcept;
std::string column_or_value_to_sql(
const dynamic::ColumnOrValue& _col) const noexcept;
std::string column_to_sql_definition(
const dynamic::Column& _col) const noexcept;
std::string condition_to_sql(
const dynamic::Condition& _condition) const noexcept;
template <class ConditionType>
std::string condition_to_sql_impl(
const ConditionType& _condition) const noexcept;
std::string create_table_to_sql(
const dynamic::CreateTable& _stmt) const noexcept;
@@ -89,6 +99,51 @@ class Connection : public sqlgen::Connection {
Credentials credentials_;
};
template <class ConditionType>
std::string Connection::condition_to_sql_impl(
const ConditionType& _condition) const noexcept {
using C = std::remove_cvref_t<ConditionType>;
std::stringstream stream;
if constexpr (std::is_same_v<C, dynamic::Condition::And>) {
stream << "(" << condition_to_sql(*_condition.cond1) << ") AND ("
<< condition_to_sql(*_condition.cond2) << ")";
} else if constexpr (std::is_same_v<C, dynamic::Condition::Equal>) {
stream << column_or_value_to_sql(_condition.op1) << " = "
<< column_or_value_to_sql(_condition.op2);
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterEqual>) {
stream << column_or_value_to_sql(_condition.op1)
<< " >= " << column_or_value_to_sql(_condition.op2);
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterThan>) {
stream << column_or_value_to_sql(_condition.op1) << " > "
<< column_or_value_to_sql(_condition.op2);
} else if constexpr (std::is_same_v<C, dynamic::Condition::NotEqual>) {
stream << column_or_value_to_sql(_condition.op1)
<< " != " << column_or_value_to_sql(_condition.op2);
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserEqual>) {
stream << column_or_value_to_sql(_condition.op1)
<< " <= " << column_or_value_to_sql(_condition.op2);
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserThan>) {
stream << column_or_value_to_sql(_condition.op1) << " < "
<< column_or_value_to_sql(_condition.op2);
} else if constexpr (std::is_same_v<C, dynamic::Condition::Or>) {
stream << "(" << condition_to_sql(*_condition.cond1) << ") OR ("
<< condition_to_sql(*_condition.cond2) << ")";
} else {
static_assert(rfl::always_false_v<C>, "Not all cases where covered.");
}
return stream.str();
}
} // namespace sqlgen::postgres
#endif
+4
View File
@@ -1,6 +1,7 @@
#ifndef SQLGEN_TRANSPILATION_VALUE_HPP_
#define SQLGEN_TRANSPILATION_VALUE_HPP_
#include <string>
#include <type_traits>
namespace sqlgen::transpilation {
@@ -15,6 +16,9 @@ auto make_value(T&& _t) {
return Value<std::remove_cvref_t<T>>{.val = _t};
}
inline auto make_value(const char* _str) {
return Value<std::string>{.val = _str};
}
} // namespace sqlgen::transpilation
#endif
+69 -16
View File
@@ -10,6 +10,7 @@
#include "../dynamic/Condition.hpp"
#include "Condition.hpp"
#include "conditions.hpp"
#include "to_value.hpp"
namespace sqlgen::transpilation {
@@ -25,8 +26,7 @@ struct ToCondition<T, Condition<CondType>> {
template <class T, class CondType1, class CondType2>
struct ToCondition<T, conditions::And<CondType1, CondType2>> {
dynamic::Condition operator()(
const conditions::And<CondType1, CondType2>& _cond) const {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{
.val = dynamic::Condition::And{
.cond1 = Ref<dynamic::Condition>::make(
@@ -40,8 +40,7 @@ struct ToCondition<T, conditions::And<CondType1, CondType2>> {
template <class T, rfl::internal::StringLiteral _name1,
rfl::internal::StringLiteral _name2>
struct ToCondition<T, conditions::Equal<Col<_name1>, Col<_name2>>> {
dynamic::Condition operator()(
const conditions::Equal<Col<_name1>, Col<_name2>>& _cond) const {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::Equal{
.op1 = dynamic::Column{.name = _name1.str()},
.op2 = dynamic::Column{.name = _name2.str()},
@@ -49,11 +48,20 @@ 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>>> {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::Equal{
.op1 = dynamic::Column{.name = _name.str()},
.op2 = to_value(_cond.op2.val),
}};
}
};
template <class T, rfl::internal::StringLiteral _name1,
rfl::internal::StringLiteral _name2>
struct ToCondition<T, conditions::GreaterEqual<Col<_name1>, Col<_name2>>> {
dynamic::Condition operator()(
const conditions::GreaterEqual<Col<_name1>, Col<_name2>>& _cond) const {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::GreaterEqual{
.op1 = dynamic::Column{.name = _name1.str()},
.op2 = dynamic::Column{.name = _name2.str()},
@@ -61,11 +69,20 @@ 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>>> {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::GreaterEqual{
.op1 = dynamic::Column{.name = _name.str()},
.op2 = to_value(_cond.op2.val),
}};
}
};
template <class T, rfl::internal::StringLiteral _name1,
rfl::internal::StringLiteral _name2>
struct ToCondition<T, conditions::GreaterThan<Col<_name1>, Col<_name2>>> {
dynamic::Condition operator()(
const conditions::GreaterThan<Col<_name1>, Col<_name2>>& _cond) const {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::GreaterThan{
.op1 = dynamic::Column{.name = _name1.str()},
.op2 = dynamic::Column{.name = _name2.str()},
@@ -73,11 +90,20 @@ 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>>> {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::GreaterThan{
.op1 = dynamic::Column{.name = _name.str()},
.op2 = to_value(_cond.op2.val),
}};
}
};
template <class T, rfl::internal::StringLiteral _name1,
rfl::internal::StringLiteral _name2>
struct ToCondition<T, conditions::LesserEqual<Col<_name1>, Col<_name2>>> {
dynamic::Condition operator()(
const conditions::LesserEqual<Col<_name1>, Col<_name2>>& _cond) const {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::LesserEqual{
.op1 = dynamic::Column{.name = _name1.str()},
.op2 = dynamic::Column{.name = _name2.str()},
@@ -85,11 +111,20 @@ 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>>> {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::LesserEqual{
.op1 = dynamic::Column{.name = _name.str()},
.op2 = to_value(_cond.op2.val),
}};
}
};
template <class T, rfl::internal::StringLiteral _name1,
rfl::internal::StringLiteral _name2>
struct ToCondition<T, conditions::LesserThan<Col<_name1>, Col<_name2>>> {
dynamic::Condition operator()(
const conditions::LesserThan<Col<_name1>, Col<_name2>>& _cond) const {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::LesserThan{
.op1 = dynamic::Column{.name = _name1.str()},
.op2 = dynamic::Column{.name = _name2.str()},
@@ -97,11 +132,20 @@ 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>>> {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::LesserThan{
.op1 = dynamic::Column{.name = _name.str()},
.op2 = to_value(_cond.op2.val),
}};
}
};
template <class T, rfl::internal::StringLiteral _name1,
rfl::internal::StringLiteral _name2>
struct ToCondition<T, conditions::NotEqual<Col<_name1>, Col<_name2>>> {
dynamic::Condition operator()(
const conditions::NotEqual<Col<_name1>, Col<_name2>>& _cond) const {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::NotEqual{
.op1 = dynamic::Column{.name = _name1.str()},
.op2 = dynamic::Column{.name = _name2.str()},
@@ -109,10 +153,19 @@ 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>>> {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{.val = dynamic::Condition::NotEqual{
.op1 = dynamic::Column{.name = _name.str()},
.op2 = to_value(_cond.op2.val),
}};
}
};
template <class T, class CondType1, class CondType2>
struct ToCondition<T, conditions::Or<CondType1, CondType2>> {
dynamic::Condition operator()(
const conditions::Or<CondType1, CondType2>& _cond) const {
dynamic::Condition operator()(const auto& _cond) const {
return dynamic::Condition{
.val = dynamic::Condition::Or{
.cond1 = Ref<dynamic::Condition>::make(
+35
View File
@@ -0,0 +1,35 @@
#ifndef SQLGEN_TRANSPILATION_TO_VALUE_HPP_
#define SQLGEN_TRANSPILATION_TO_VALUE_HPP_
#include <rfl.hpp>
#include <type_traits>
#include "../dynamic/Value.hpp"
#include "Value.hpp"
#include "has_reflection_method.hpp"
namespace sqlgen::transpilation {
template <class T>
dynamic::Value to_value(const T& _t) {
using Type = std::remove_cvref_t<T>;
if constexpr (std::is_floating_point_v<Type>) {
return dynamic::Float{.val = static_cast<double>(_t)};
} else if constexpr (std::is_integral_v<Type>) {
return dynamic::Integer{.val = static_cast<int64_t>(_t)};
} else if constexpr (std::is_convertible_v<Type, std::string>) {
return dynamic::String{.val = std::string(_t)};
} else if constexpr (has_reflection_method<Type>) {
return to_value(_t.reflection());
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type");
}
}
} // namespace sqlgen::transpilation
#endif
+2 -2
View File
@@ -23,9 +23,9 @@ auto operator|(const Read<ContainerType, WhereType, OrderByType, LimitType>& _r,
"You cannot call where(...) twice (but you can apply more "
"than one condition by combining them with && or ||).");
static_assert(std::is_same_v<OrderByType, Nothing>,
"You must call order_by(...) after where(...).");
"You cannot call order_by(...) before where(...).");
static_assert(std::is_same_v<LimitType, Nothing>,
"You must call limit(...) after where(...).");
"You cannot call limit(...) before where(...).");
return Read<ContainerType, ConditionType, OrderByType, LimitType>{
.where_ = _where.condition};
}
-2
View File
@@ -143,8 +143,6 @@ std::string Connection::properties_to_sql(
Result<Ref<IteratorBase>> Connection::read(const dynamic::SelectFrom& _query) {
const auto sql = to_sql(_query);
std::cout << sql << std::endl;
sqlite3_stmt* p_stmt = nullptr;
sqlite3_prepare(conn_.get(), /* Database handle */
+1 -1
View File
@@ -33,7 +33,7 @@ TEST(sqlite, test_limit) {
using namespace sqlgen;
const auto query =
sqlgen::read<std::vector<Person>> | order_by(col<"age">) | limit(2);
sqlgen::read<std::vector<Person>> | order_by("age"_c) | limit(2);
const auto people2 = query(conn).value();
+1 -1
View File
@@ -33,7 +33,7 @@ TEST(sqlite, test_order_by) {
using namespace sqlgen;
const auto query = sqlgen::read<std::vector<Person>> |
order_by(col<"age">, col<"first_name">.desc());
order_by("age"_c, "first_name"_c.desc());
const auto people2 = query(conn).value();
+4 -5
View File
@@ -32,15 +32,14 @@ TEST(sqlite, test_where) {
using namespace sqlgen;
const auto query =
sqlgen::read<std::vector<Person>> |
where(col<"first_name"> != col<"last_name"> or col<"id"> != col<"age">) |
order_by(col<"age">, col<"first_name">.desc());
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":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":4,"first_name":"Hugo","last_name":"Simpson","age":10},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10},{"id":0,"first_name":"Homer","last_name":"Simpson","age":45}])";
R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])";
EXPECT_EQ(rfl::json::write(people2), expected);
}