mirror of
https://github.com/getml/sqlgen.git
synced 2026-05-24 01:38:23 -05:00
Added aggregations and GROUP BY (#17)
This commit is contained in:
committed by
GitHub
parent
21564cacf6
commit
775f15babf
@@ -14,6 +14,8 @@
|
||||
#include "sqlgen/Session.hpp"
|
||||
#include "sqlgen/Timestamp.hpp"
|
||||
#include "sqlgen/Varchar.hpp"
|
||||
#include "sqlgen/aggregations.hpp"
|
||||
#include "sqlgen/as.hpp"
|
||||
#include "sqlgen/begin_transaction.hpp"
|
||||
#include "sqlgen/col.hpp"
|
||||
#include "sqlgen/commit.hpp"
|
||||
@@ -22,6 +24,7 @@
|
||||
#include "sqlgen/delete_from.hpp"
|
||||
#include "sqlgen/drop.hpp"
|
||||
#include "sqlgen/exec.hpp"
|
||||
#include "sqlgen/group_by.hpp"
|
||||
#include "sqlgen/if_exists.hpp"
|
||||
#include "sqlgen/if_not_exists.hpp"
|
||||
#include "sqlgen/insert.hpp"
|
||||
@@ -31,6 +34,8 @@
|
||||
#include "sqlgen/patterns.hpp"
|
||||
#include "sqlgen/read.hpp"
|
||||
#include "sqlgen/rollback.hpp"
|
||||
#include "sqlgen/select_from.hpp"
|
||||
#include "sqlgen/to.hpp"
|
||||
#include "sqlgen/update.hpp"
|
||||
#include "sqlgen/where.hpp"
|
||||
#include "sqlgen/write.hpp"
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef SQLGEN_AGGREGATIONS_HPP_
|
||||
#define SQLGEN_AGGREGATIONS_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "col.hpp"
|
||||
#include "transpilation/aggregations.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto avg(const Col<_name>&) {
|
||||
return transpilation::aggregations::Avg<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
}
|
||||
|
||||
inline auto count() {
|
||||
return transpilation::aggregations::Count<transpilation::aggregations::All>{};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto count(const Col<_name>&) {
|
||||
return transpilation::aggregations::Count<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto count_distinct(const Col<_name>&) {
|
||||
return transpilation::aggregations::Count<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}, .distinct = true};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto max(const Col<_name>&) {
|
||||
return transpilation::aggregations::Max<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto min(const Col<_name>&) {
|
||||
return transpilation::aggregations::Min<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
auto sum(const Col<_name>&) {
|
||||
return transpilation::aggregations::Sum<transpilation::Col<_name>>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
}
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef SQLGEN_AS_HPP_
|
||||
#define SQLGEN_AS_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "col.hpp"
|
||||
#include "transpilation/As.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
struct As {
|
||||
template <class ValueType>
|
||||
auto operator()(const ValueType& _val) const noexcept {
|
||||
if constexpr (requires(ValueType v) { v.template as<"new_name">(); }) {
|
||||
return _val.template as<_new_name>();
|
||||
} else {
|
||||
return transpilation::As<std::remove_cvref_t<ValueType>, _new_name>{
|
||||
.val = _val};
|
||||
}
|
||||
}
|
||||
|
||||
auto operator()(const char* _val) const noexcept {
|
||||
return transpilation::As<std::string, _new_name>{.val = _val};
|
||||
}
|
||||
};
|
||||
|
||||
template <class ValueType, rfl::internal::StringLiteral _new_name>
|
||||
auto operator|(const ValueType& _val, const As<_new_name>& _as) {
|
||||
return _as(_val);
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
const auto as = As<_new_name>{};
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "transpilation/As.hpp"
|
||||
#include "transpilation/Col.hpp"
|
||||
#include "transpilation/Condition.hpp"
|
||||
#include "transpilation/Desc.hpp"
|
||||
@@ -18,6 +19,12 @@ struct Col {
|
||||
using ColType = transpilation::Col<_name>;
|
||||
using Name = rfl::Literal<_name>;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
return transpilation::As<transpilation::Col<_name>, _new_name>{
|
||||
.val = transpilation::Col<_name>{}};
|
||||
}
|
||||
|
||||
/// Signals to order_by that this column is to be sorted in descending order.
|
||||
auto desc() const noexcept {
|
||||
return transpilation::Desc<transpilation::Col<_name>>{};
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef SQLGEN_DYNAMIC_AGGREGATION_HPP_
|
||||
#define SQLGEN_DYNAMIC_AGGREGATION_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Column.hpp"
|
||||
#include "ColumnOrValue.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
struct Aggregation {
|
||||
struct Avg {
|
||||
ColumnOrValue val;
|
||||
};
|
||||
|
||||
struct Count {
|
||||
std::optional<Column> val;
|
||||
bool distinct = false;
|
||||
};
|
||||
|
||||
struct Max {
|
||||
ColumnOrValue val;
|
||||
};
|
||||
|
||||
struct Min {
|
||||
ColumnOrValue val;
|
||||
};
|
||||
|
||||
struct Sum {
|
||||
ColumnOrValue val;
|
||||
};
|
||||
|
||||
using ReflectionType = rfl::TaggedUnion<"what", Avg, Count, Max, Min, Sum>;
|
||||
|
||||
const ReflectionType& reflection() const { return val; }
|
||||
|
||||
ReflectionType val;
|
||||
};
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef SQLGEN_DYNAMIC_COLUMNORAGGREGATION_HPP_
|
||||
#define SQLGEN_DYNAMIC_COLUMNORAGGREGATION_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
|
||||
#include "Aggregation.hpp"
|
||||
#include "Column.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
using ColumnOrAggregation = rfl::TaggedUnion<"type", Column, Aggregation>;
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef SQLGEN_DYNAMIC_GROUPBY_HPP_
|
||||
#define SQLGEN_DYNAMIC_GROUPBY_HPP_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Column.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
struct GroupBy {
|
||||
std::vector<Column> columns;
|
||||
};
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
#endif
|
||||
@@ -2,21 +2,31 @@
|
||||
#define SQLGEN_DYNAMIC_SELECTFROM_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Aggregation.hpp"
|
||||
#include "Column.hpp"
|
||||
#include "Condition.hpp"
|
||||
#include "GroupBy.hpp"
|
||||
#include "Limit.hpp"
|
||||
#include "OrderBy.hpp"
|
||||
#include "Table.hpp"
|
||||
#include "Value.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
struct SelectFrom {
|
||||
struct Field {
|
||||
rfl::TaggedUnion<"type", Aggregation, Column, Value> val;
|
||||
std::optional<std::string> as;
|
||||
};
|
||||
|
||||
Table table;
|
||||
std::vector<Column> columns;
|
||||
std::vector<Field> fields;
|
||||
std::optional<Condition> where = std::nullopt;
|
||||
std::optional<GroupBy> group_by = std::nullopt;
|
||||
std::optional<OrderBy> order_by = std::nullopt;
|
||||
std::optional<Limit> limit = std::nullopt;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef SQLGEN_GROUP_BY_HPP_
|
||||
#define SQLGEN_GROUP_BY_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "Result.hpp"
|
||||
#include "select_from.hpp"
|
||||
#include "transpilation/group_by_t.hpp"
|
||||
#include "transpilation/value_t.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class... ColTypes>
|
||||
struct GroupBy {};
|
||||
|
||||
template <class StructType, class FieldsTupleType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType, class ToType,
|
||||
class... ColTypes>
|
||||
auto operator|(
|
||||
const SelectFrom<StructType, FieldsTupleType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, ToType>& _s,
|
||||
const GroupBy<ColTypes...>&) {
|
||||
static_assert(
|
||||
std::is_same_v<GroupByType, Nothing>,
|
||||
"You cannot call group_by(...) twice (but you can group by more "
|
||||
"than one column).");
|
||||
static_assert(std::is_same_v<OrderByType, Nothing>,
|
||||
"You cannot call order_by(...) before group_by(...).");
|
||||
static_assert(std::is_same_v<LimitType, Nothing>,
|
||||
"You cannot call limit(...) before group_by(...).");
|
||||
static_assert(std::is_same_v<ToType, Nothing>,
|
||||
"You cannot call to<...> before group_by(...).");
|
||||
static_assert(sizeof...(ColTypes) != 0,
|
||||
"You must assign at least one column to group_by.");
|
||||
return SelectFrom<
|
||||
StructType, FieldsTupleType, WhereType,
|
||||
transpilation::group_by_t<StructType, typename ColTypes::ColType...>,
|
||||
OrderByType, LimitType, ToType>{.fields_ = _s.fields_,
|
||||
.where_ = _s.where_};
|
||||
}
|
||||
|
||||
template <class... ColTypes>
|
||||
auto group_by(const ColTypes&...) {
|
||||
return GroupBy<ColTypes...>{};
|
||||
};
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef SQLGEN_INTERNAL_GETCOLTYPE_HPP_
|
||||
#define SQLGEN_INTERNAL_GETCOLTYPE_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
|
||||
#include "../col.hpp"
|
||||
#include "../transpilation/Col.hpp"
|
||||
|
||||
namespace sqlgen::internal {
|
||||
|
||||
template <class T>
|
||||
struct GetColType;
|
||||
|
||||
template <class T>
|
||||
struct GetColType {
|
||||
using Type = T;
|
||||
static Type get_value(const T& _t) { return _t; }
|
||||
};
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
struct GetColType<Col<_name>> {
|
||||
using Type = transpilation::Col<_name>;
|
||||
static Type get_value(const auto&) { return transpilation::Col<_name>{}; }
|
||||
};
|
||||
|
||||
} // namespace sqlgen::internal
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef SQLGEN_INTERNAL_CALL_DESTRUCTORS_WHERE_NECESSARY_HPP_
|
||||
#define SQLGEN_INTERNAL_CALL_DESTRUCTORS_WHERE_NECESSARY_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sqlgen::internal {
|
||||
|
||||
/// Because of the way we have allocated the fields, we need to manually
|
||||
/// trigger the destructors.
|
||||
template <class ViewType, size_t _size, size_t _i>
|
||||
void call_destructor_on_one_if_necessary(const size_t _num_fields_assigned,
|
||||
ViewType* _view) {
|
||||
using FieldType = rfl::tuple_element_t<_i, typename ViewType::Fields>;
|
||||
using OriginalType = std::remove_cv_t<typename FieldType::Type>;
|
||||
using ValueType =
|
||||
std::remove_cvref_t<std::remove_pointer_t<typename FieldType::Type>>;
|
||||
if constexpr (std::is_pointer_v<OriginalType> &&
|
||||
std::is_destructible_v<ValueType>) {
|
||||
if (_i < _num_fields_assigned) {
|
||||
rfl::get<_i>(*_view)->~ValueType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ViewType>
|
||||
void call_destructors_where_necessary(const size_t _num_fields_assigned,
|
||||
ViewType* _view) {
|
||||
constexpr size_t size = ViewType::size();
|
||||
[&]<size_t... is>(std::integer_sequence<size_t, is...>) {
|
||||
(call_destructor_on_one_if_necessary<ViewType, size, is>(
|
||||
_num_fields_assigned, _view),
|
||||
...);
|
||||
}(std::make_integer_sequence<size_t, size>());
|
||||
}
|
||||
|
||||
} // namespace sqlgen::internal
|
||||
|
||||
#endif
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../parsing/Parser.hpp"
|
||||
#include "call_destructors_where_necessary.hpp"
|
||||
|
||||
namespace sqlgen::internal {
|
||||
|
||||
@@ -34,7 +35,7 @@ void assign_if_field_is_field_i(
|
||||
*_err = Error(stream.str());
|
||||
return;
|
||||
}
|
||||
*rfl::get<i>(*_view) = std::move(*res);
|
||||
::new (rfl::get<i>(*_view)) T(std::move(*res));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,13 +72,17 @@ std::pair<std::optional<Error>, size_t> read_into_view(
|
||||
template <class T>
|
||||
Result<T> from_str_vec(
|
||||
const std::vector<std::optional<std::string>>& _str_vec) {
|
||||
T t;
|
||||
auto view = rfl::to_view(t);
|
||||
alignas(T) unsigned char buf[sizeof(T)]{};
|
||||
auto ptr = rfl::internal::ptr_cast<T*>(&buf);
|
||||
auto view = rfl::to_view(*ptr);
|
||||
const auto [err, num_fields_assigned] = read_into_view(_str_vec, &view);
|
||||
if (err) {
|
||||
if (err) [[unlikely]] {
|
||||
call_destructors_where_necessary(num_fields_assigned, &view);
|
||||
return error(err->what());
|
||||
}
|
||||
return t;
|
||||
auto res = Result<T>(std::move(*ptr));
|
||||
call_destructors_where_necessary(num_fields_assigned, &view);
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace sqlgen::internal
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "Result.hpp"
|
||||
#include "read.hpp"
|
||||
#include "select_from.hpp"
|
||||
#include "transpilation/Limit.hpp"
|
||||
#include "transpilation/value_t.hpp"
|
||||
|
||||
@@ -17,10 +18,22 @@ template <class ContainerType, class WhereType, class OrderByType,
|
||||
auto operator|(const Read<ContainerType, WhereType, OrderByType, LimitType>& _r,
|
||||
const Limit& _limit) {
|
||||
static_assert(std::is_same_v<LimitType, Nothing>,
|
||||
"You cannot call limit twice (but you can order by more "
|
||||
"than one column).");
|
||||
return Read<ContainerType, WhereType, OrderByType, Limit>{
|
||||
.where_ = _r.where_, .order_by_ = _r.order_by_, .limit_ = _limit};
|
||||
"You cannot call limit twice.");
|
||||
return Read<ContainerType, WhereType, OrderByType, Limit>{.where_ = _r.where_,
|
||||
.limit_ = _limit};
|
||||
}
|
||||
|
||||
template <class StructType, class FieldsTupleType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType, class ToType>
|
||||
auto operator|(
|
||||
const SelectFrom<StructType, FieldsTupleType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, ToType>& _s,
|
||||
const Limit& _limit) {
|
||||
static_assert(std::is_same_v<LimitType, Nothing>,
|
||||
"You cannot call limit twice.");
|
||||
return SelectFrom<StructType, FieldsTupleType, WhereType, GroupByType,
|
||||
OrderByType, Limit, ToType>{
|
||||
.fields_ = _s.fields_, .where_ = _s.where_, .limit_ = _limit};
|
||||
}
|
||||
|
||||
inline auto limit(const size_t _val) { return Limit{_val}; };
|
||||
|
||||
@@ -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>,
|
||||
"You cannot call limit(...) must be called before order_by.");
|
||||
"You cannot call limit(...) before order_by.");
|
||||
static_assert(sizeof...(ColTypes) != 0,
|
||||
"You must assign at least one column to order by.");
|
||||
return Read<ContainerType, WhereType,
|
||||
@@ -31,6 +31,30 @@ auto operator|(const Read<ContainerType, WhereType, OrderByType, LimitType>& _r,
|
||||
LimitType>{.where_ = _r.where_};
|
||||
}
|
||||
|
||||
template <class StructType, class FieldsTupleType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType, class ToType,
|
||||
class... ColTypes>
|
||||
auto operator|(
|
||||
const SelectFrom<StructType, FieldsTupleType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, ToType>& _s,
|
||||
const OrderBy<ColTypes...>&) {
|
||||
static_assert(
|
||||
std::is_same_v<OrderByType, Nothing>,
|
||||
"You cannot call order_by(...) twice (but you can order by more "
|
||||
"than one column).");
|
||||
static_assert(std::is_same_v<LimitType, Nothing>,
|
||||
"You cannot call limit(...) before order_by(...).");
|
||||
static_assert(std::is_same_v<ToType, Nothing>,
|
||||
"You cannot call to<...> before order_by(...).");
|
||||
static_assert(sizeof...(ColTypes) != 0,
|
||||
"You must assign at least one column to order_by.");
|
||||
return SelectFrom<
|
||||
StructType, FieldsTupleType, WhereType, GroupByType,
|
||||
transpilation::order_by_t<
|
||||
StructType, typename std::remove_cvref_t<ColTypes>::ColType...>,
|
||||
LimitType, ToType>{.fields_ = _s.fields_, .where_ = _s.where_};
|
||||
}
|
||||
|
||||
template <class... ColTypes>
|
||||
auto order_by(const ColTypes&...) {
|
||||
return OrderBy<ColTypes...>{};
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "Result.hpp"
|
||||
#include "internal/is_range.hpp"
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/to_select_from.hpp"
|
||||
#include "transpilation/read_to_select_from.hpp"
|
||||
#include "transpilation/value_t.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
@@ -23,8 +23,8 @@ Result<ContainerType> read_impl(const Ref<Connection>& _conn,
|
||||
using ValueType = transpilation::value_t<ContainerType>;
|
||||
if constexpr (internal::is_range_v<ContainerType>) {
|
||||
const auto query =
|
||||
transpilation::to_select_from<ValueType, WhereType, OrderByType,
|
||||
LimitType>(_where, _limit);
|
||||
transpilation::read_to_select_from<ValueType, WhereType, OrderByType,
|
||||
LimitType>(_where, _limit);
|
||||
return _conn->read(query).transform(
|
||||
[](auto&& _it) { return ContainerType(_it); });
|
||||
|
||||
@@ -86,8 +86,6 @@ struct Read {
|
||||
|
||||
WhereType where_;
|
||||
|
||||
OrderByType order_by_;
|
||||
|
||||
LimitType limit_;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
#ifndef SQLGEN_SELECT_FROM_HPP_
|
||||
#define SQLGEN_SELECT_FROM_HPP_
|
||||
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Range.hpp"
|
||||
#include "Ref.hpp"
|
||||
#include "Result.hpp"
|
||||
#include "col.hpp"
|
||||
#include "internal/GetColType.hpp"
|
||||
#include "internal/is_range.hpp"
|
||||
#include "is_connection.hpp"
|
||||
#include "transpilation/fields_to_named_tuple_t.hpp"
|
||||
#include "transpilation/to_select_from.hpp"
|
||||
#include "transpilation/value_t.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class StructType, class FieldsType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType,
|
||||
class ContainerType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
auto select_from_impl(const Ref<Connection>& _conn, const FieldsType& _fields,
|
||||
const WhereType& _where, const LimitType& _limit) {
|
||||
if constexpr (internal::is_range_v<ContainerType>) {
|
||||
const auto query =
|
||||
transpilation::to_select_from<StructType, FieldsType, WhereType,
|
||||
GroupByType, OrderByType, LimitType>(
|
||||
_fields, _where, _limit);
|
||||
return _conn->read(query).transform(
|
||||
[](auto&& _it) { return ContainerType(_it); });
|
||||
|
||||
} else {
|
||||
const auto to_container = [](auto range) -> Result<ContainerType> {
|
||||
using ValueType = transpilation::value_t<ContainerType>;
|
||||
ContainerType container;
|
||||
for (auto& res : range) {
|
||||
if (res) {
|
||||
container.emplace_back(
|
||||
rfl::from_named_tuple<ValueType>(std::move(*res)));
|
||||
} else {
|
||||
return error(res.error().what());
|
||||
}
|
||||
}
|
||||
return container;
|
||||
};
|
||||
|
||||
using RangeType =
|
||||
Range<transpilation::fields_to_named_tuple_t<StructType, FieldsType>>;
|
||||
|
||||
return select_from_impl<StructType, FieldsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, RangeType>(_conn, _fields,
|
||||
_where, _limit)
|
||||
.and_then(to_container);
|
||||
}
|
||||
}
|
||||
|
||||
template <class StructType, class FieldsType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType,
|
||||
class ContainerType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
auto select_from_impl(const Result<Ref<Connection>>& _res,
|
||||
const FieldsType& _fields, const WhereType& _where,
|
||||
const LimitType& _limit) {
|
||||
return _res.and_then([&](const auto& _conn) {
|
||||
return select_from_impl<StructType, FieldsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, ContainerType>(
|
||||
_conn, _where, _limit);
|
||||
});
|
||||
}
|
||||
|
||||
template <class StructType, class FieldsType, class WhereType = Nothing,
|
||||
class GroupByType = Nothing, class OrderByType = Nothing,
|
||||
class LimitType = Nothing, class ToType = Nothing>
|
||||
struct SelectFrom {
|
||||
auto operator()(const auto& _conn) const {
|
||||
if constexpr (std::is_same_v<ToType, Nothing> ||
|
||||
std::ranges::input_range<std::remove_cvref_t<ToType>>) {
|
||||
using ContainerType = std::conditional_t<
|
||||
std::is_same_v<ToType, Nothing>,
|
||||
Range<transpilation::fields_to_named_tuple_t<StructType, FieldsType>>,
|
||||
ToType>;
|
||||
return select_from_impl<StructType, FieldsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, ContainerType>(
|
||||
_conn, fields_, where_, limit_);
|
||||
|
||||
} else {
|
||||
const auto extract_result = [](auto&& _vec) -> Result<ToType> {
|
||||
if (_vec.size() != 1) {
|
||||
return error(
|
||||
"Because the type provided to to<...> was not a container, the "
|
||||
"query needs to return exactly one result, but it did return " +
|
||||
std::to_string(_vec.size()) + " results.");
|
||||
}
|
||||
return std::move(_vec[0]);
|
||||
};
|
||||
|
||||
return select_from_impl<StructType, FieldsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType,
|
||||
std::vector<std::remove_cvref_t<ToType>>>(
|
||||
_conn, fields_, where_, limit_)
|
||||
.and_then(extract_result);
|
||||
}
|
||||
}
|
||||
|
||||
FieldsType fields_;
|
||||
|
||||
WhereType where_;
|
||||
|
||||
LimitType limit_;
|
||||
};
|
||||
|
||||
template <class StructType, class... FieldTypes>
|
||||
inline auto select_from(const FieldTypes&... _fields) {
|
||||
using FieldsType =
|
||||
rfl::Tuple<typename internal::GetColType<FieldTypes>::Type...>;
|
||||
return SelectFrom<StructType, FieldsType>{
|
||||
.fields_ =
|
||||
FieldsType(internal::GetColType<FieldTypes>::get_value(_fields)...)};
|
||||
}
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef SQLGEN_TO_HPP_
|
||||
#define SQLGEN_TO_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "Result.hpp"
|
||||
#include "select_from.hpp"
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class NewToType>
|
||||
struct To {};
|
||||
|
||||
template <class StructType, class FieldsTupleType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType, class ToType,
|
||||
class NewToType>
|
||||
auto operator|(
|
||||
const SelectFrom<StructType, FieldsTupleType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, ToType>& _s,
|
||||
const To<NewToType>&) {
|
||||
static_assert(std::is_same_v<ToType, Nothing>,
|
||||
"You cannot call to<...> twice.");
|
||||
return SelectFrom<StructType, FieldsTupleType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, NewToType>{
|
||||
.fields_ = _s.fields_, .where_ = _s.where_, .limit_ = _s.limit_};
|
||||
}
|
||||
|
||||
template <class NewToType>
|
||||
const auto to = To<NewToType>{};
|
||||
|
||||
} // namespace sqlgen
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_AS_HPP_
|
||||
#define SQLGEN_TRANSPILATION_AS_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
|
||||
#include "../Literal.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class _ValueType, rfl::internal::StringLiteral _new_name>
|
||||
struct As {
|
||||
using ValueType = _ValueType;
|
||||
using NewNameType = Literal<_new_name>;
|
||||
|
||||
ValueType val;
|
||||
};
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_AGGREGATIONS_HPP_
|
||||
#define SQLGEN_TRANSPILATION_AGGREGATIONS_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "As.hpp"
|
||||
|
||||
namespace sqlgen::transpilation::aggregations {
|
||||
|
||||
/// To be used when we want to count everything.
|
||||
struct All {};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Avg {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Count {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
bool distinct = false;
|
||||
};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Max {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Min {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
};
|
||||
|
||||
template <class _ValueType>
|
||||
struct Sum {
|
||||
using ValueType = _ValueType;
|
||||
|
||||
template <rfl::internal::StringLiteral _new_name>
|
||||
auto as() const noexcept {
|
||||
using T = std::remove_cvref_t<decltype(*this)>;
|
||||
return transpilation::As<T, _new_name>{.val = *this};
|
||||
}
|
||||
|
||||
ValueType val;
|
||||
};
|
||||
|
||||
} // namespace sqlgen::transpilation::aggregations
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_CHECKAGGREGATIONS_HPP_
|
||||
#define SQLGEN_TRANSPILATION_CHECKAGGREGATIONS_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Literal.hpp"
|
||||
#include "Col.hpp"
|
||||
#include "group_by_t.hpp"
|
||||
#include "make_field.hpp"
|
||||
#include "remove_as_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class StructType, class FieldsType, class GroupByType>
|
||||
struct CheckAggregation;
|
||||
|
||||
/// Case: No aggregation, no group by.
|
||||
template <class StructType, class... FieldTypes>
|
||||
requires(true && ... && !MakeField<StructType, FieldTypes>::is_aggregation)
|
||||
struct CheckAggregation<StructType, rfl::Tuple<FieldTypes...>, Nothing> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
/// Case: At least one aggregation, no group by.
|
||||
template <class StructType, class... FieldTypes>
|
||||
requires(false || ... || MakeField<StructType, FieldTypes>::is_aggregation)
|
||||
struct CheckAggregation<StructType, rfl::Tuple<FieldTypes...>, Nothing> {
|
||||
static constexpr bool value =
|
||||
(true && ... &&
|
||||
(MakeField<StructType, FieldTypes>::is_aggregation ||
|
||||
!MakeField<StructType, FieldTypes>::is_column));
|
||||
static_assert(
|
||||
value,
|
||||
"If any column is aggregated and there is no GROUP BY, then all columns "
|
||||
"must be aggregated.");
|
||||
};
|
||||
|
||||
/// Case: There is a group by.
|
||||
template <class StructType, class... FieldTypes, class... ColTypes>
|
||||
struct CheckAggregation<StructType, rfl::Tuple<FieldTypes...>,
|
||||
GroupBy<ColTypes...>> {
|
||||
template <class F>
|
||||
static constexpr bool included_in_group_by =
|
||||
(false || ... ||
|
||||
std::is_same_v<std::remove_cvref_t<remove_as_t<F>>,
|
||||
std::remove_cvref_t<ColTypes>>);
|
||||
|
||||
static constexpr bool value =
|
||||
(true && ... &&
|
||||
(MakeField<StructType, FieldTypes>::is_aggregation ||
|
||||
!MakeField<StructType, FieldTypes>::is_column ||
|
||||
included_in_group_by<FieldTypes>));
|
||||
|
||||
static_assert(value,
|
||||
"If there is a GROUP BY, then all columns "
|
||||
"must either be aggregated or included inside the GROUP BY.");
|
||||
};
|
||||
|
||||
template <class StructType, class FieldsType, class GroupByType>
|
||||
consteval bool check_aggregations() {
|
||||
return CheckAggregation<std::remove_cvref_t<StructType>,
|
||||
std::remove_cvref_t<FieldsType>,
|
||||
std::remove_cvref_t<GroupByType>>::value;
|
||||
}
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_FIELDSTONAMEDTUPLET_HPP_
|
||||
#define SQLGEN_TRANSPILATION_FIELDSTONAMEDTUPLET_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
|
||||
#include "../Literal.hpp"
|
||||
#include "make_field.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T>
|
||||
struct GetFieldName;
|
||||
|
||||
template <class T>
|
||||
struct GetFieldName {
|
||||
static_assert(
|
||||
rfl::always_false_v<T>,
|
||||
"You must declare a field name for this field using sqlgen::as.");
|
||||
};
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
struct GetFieldName<Literal<_name>> {
|
||||
static constexpr rfl::internal::StringLiteral name = _name;
|
||||
};
|
||||
|
||||
template <class StructType, class... FieldTypes>
|
||||
struct FieldsToNamedTupleType;
|
||||
|
||||
template <class StructType, class... FieldTypes>
|
||||
struct FieldsToNamedTupleType {
|
||||
using Type = rfl::NamedTuple<rfl::Field<
|
||||
GetFieldName<typename MakeField<StructType, FieldTypes>::Name>::name,
|
||||
typename MakeField<StructType, FieldTypes>::Type>...>;
|
||||
};
|
||||
|
||||
template <class StructType, class... FieldTypes>
|
||||
struct FieldsToNamedTupleType<StructType, rfl::Tuple<FieldTypes...>> {
|
||||
using Type = typename FieldsToNamedTupleType<StructType, FieldTypes...>::Type;
|
||||
};
|
||||
|
||||
template <class StructType, class... FieldTypes>
|
||||
using fields_to_named_tuple_t =
|
||||
typename FieldsToNamedTupleType<StructType, FieldTypes...>::Type;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_GROUPBYT_HPP_
|
||||
#define SQLGEN_TRANSPILATION_GROUPBYT_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "Col.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class... ColTypes>
|
||||
struct GroupBy {};
|
||||
|
||||
template <class T, class... ColTypes>
|
||||
auto make_group_by() {
|
||||
static_assert(all_columns_exist<T, ColTypes...>(),
|
||||
"A column in group_by does not exist.");
|
||||
return GroupBy<ColTypes...>{};
|
||||
}
|
||||
|
||||
template <class T, class... ColTypes>
|
||||
using group_by_t =
|
||||
std::invoke_result_t<decltype(make_group_by<T, ColTypes...>)>;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -41,7 +41,7 @@ consteval bool is_nullable() {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr bool is_nullable_v = is_nullable<T>();
|
||||
constexpr bool is_nullable_v = is_nullable<std::remove_cvref_t<T>>();
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_MAKE_FIELD_HPP_
|
||||
#define SQLGEN_TRANSPILATION_MAKE_FIELD_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Literal.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../dynamic/SelectFrom.hpp"
|
||||
#include "As.hpp"
|
||||
#include "Col.hpp"
|
||||
#include "Value.hpp"
|
||||
#include "aggregations.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
#include "remove_nullable_t.hpp"
|
||||
#include "to_value.hpp"
|
||||
#include "underlying_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class StructType, class FieldType>
|
||||
struct MakeField;
|
||||
|
||||
template <class StructType, class ValueType>
|
||||
struct MakeField {
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = false;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type = ValueType;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _val) const {
|
||||
return dynamic::SelectFrom::Field{.val = to_value(_val)};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, Col<_name>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A required column does not exist.");
|
||||
|
||||
static constexpr bool is_aggregation = false;
|
||||
static constexpr bool is_column = true;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{.val =
|
||||
dynamic::Column{.name = _name.str()}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, class ValueType,
|
||||
rfl::internal::StringLiteral _new_name>
|
||||
struct MakeField<StructType, As<ValueType, _new_name>> {
|
||||
static constexpr bool is_aggregation =
|
||||
MakeField<StructType, ValueType>::is_aggregation;
|
||||
static constexpr bool is_column = MakeField<StructType, ValueType>::is_column;
|
||||
|
||||
using Name = Literal<_new_name>;
|
||||
using Type =
|
||||
typename MakeField<StructType, std::remove_cvref_t<ValueType>>::Type;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _as) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = MakeField<StructType, std::remove_cvref_t<ValueType>>{}(_as.val)
|
||||
.val,
|
||||
.as = _new_name.str()};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Avg<Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in the AVG aggregation does not exist.");
|
||||
|
||||
static_assert(
|
||||
std::is_integral_v<
|
||||
remove_nullable_t<underlying_t<StructType, Col<_name>>>> ||
|
||||
std::is_floating_point_v<
|
||||
remove_nullable_t<underlying_t<StructType, Col<_name>>>>,
|
||||
"Values inside the AVG aggregation must be numerical.");
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Avg{
|
||||
.val = dynamic::Column{.name = _name.str()}}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Count<Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in the COUNT or COUNT_DISTINCT aggregation "
|
||||
"does not exist.");
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = size_t;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto& _agg) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Count{
|
||||
.val = dynamic::Column{.name = _name.str()},
|
||||
.distinct = _agg.distinct}},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType>
|
||||
struct MakeField<StructType, aggregations::Count<aggregations::All>> {
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
|
||||
using Name = Nothing;
|
||||
using Type = size_t;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{
|
||||
dynamic::Aggregation::Count{.val = std::nullopt, .distinct = false},
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Max<Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in the MAX aggregation does not exist.");
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Max{
|
||||
.val = dynamic::Column{.name = _name.str()}}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Min<Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in MIN aggregation does not exist.");
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Min{
|
||||
.val = dynamic::Column{.name = _name.str()}}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, rfl::internal::StringLiteral _name>
|
||||
struct MakeField<StructType, aggregations::Sum<Col<_name>>> {
|
||||
static_assert(all_columns_exist<StructType, Col<_name>>(),
|
||||
"A column required in SUM aggregation does not exist.");
|
||||
|
||||
static_assert(
|
||||
std::is_integral_v<
|
||||
remove_nullable_t<underlying_t<StructType, Col<_name>>>> ||
|
||||
std::is_floating_point_v<
|
||||
remove_nullable_t<underlying_t<StructType, Col<_name>>>>,
|
||||
"Values inside the SUM aggregation must be numerical.");
|
||||
|
||||
static constexpr bool is_aggregation = true;
|
||||
static constexpr bool is_column = true;
|
||||
|
||||
using Name = Literal<_name>;
|
||||
using Type = rfl::field_type_t<_name, StructType>;
|
||||
|
||||
dynamic::SelectFrom::Field operator()(const auto&) const {
|
||||
return dynamic::SelectFrom::Field{
|
||||
.val = dynamic::Aggregation{dynamic::Aggregation::Sum{
|
||||
.val = dynamic::Column{.name = _name.str()}}}};
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, class ValueType>
|
||||
inline dynamic::SelectFrom::Field make_field(const ValueType& _val) {
|
||||
return MakeField<std::remove_cvref_t<StructType>,
|
||||
std::remove_cvref_t<ValueType>>{}(_val);
|
||||
}
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_MAKE_FIELDS_HPP_
|
||||
#define SQLGEN_TRANSPILATION_MAKE_FIELDS_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../dynamic/SelectFrom.hpp"
|
||||
#include "make_field.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class StructType, class Fields, int... _is>
|
||||
std::vector<dynamic::SelectFrom::Field> make_fields(
|
||||
const Fields& _fields, std::integer_sequence<int, _is...>) {
|
||||
return std::vector<dynamic::SelectFrom::Field>(
|
||||
{make_field<StructType>(rfl::get<_is>(_fields))...});
|
||||
}
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -33,7 +33,7 @@ template <class T, class... ColTypes>
|
||||
auto make_order_by() {
|
||||
static_assert(
|
||||
all_columns_exist<T, typename OrderByWrapper<ColTypes>::ColType...>(),
|
||||
"All columns must exist.");
|
||||
"A column in order_by does not exist.");
|
||||
return OrderBy<OrderByWrapper<ColTypes>...>{};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_READ_TO_SELECT_FROM_HPP_
|
||||
#define SQLGEN_TRANSPILATION_READ_TO_SELECT_FROM_HPP_
|
||||
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../dynamic/ColumnOrAggregation.hpp"
|
||||
#include "../dynamic/SelectFrom.hpp"
|
||||
#include "../dynamic/Table.hpp"
|
||||
#include "../internal/collect/vector.hpp"
|
||||
#include "get_schema.hpp"
|
||||
#include "get_tablename.hpp"
|
||||
#include "make_columns.hpp"
|
||||
#include "to_condition.hpp"
|
||||
#include "to_limit.hpp"
|
||||
#include "to_order_by.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T, class WhereType = Nothing, class OrderByType = Nothing,
|
||||
class LimitType = Nothing>
|
||||
requires std::is_class_v<std::remove_cvref_t<T>> &&
|
||||
std::is_aggregate_v<std::remove_cvref_t<T>>
|
||||
dynamic::SelectFrom read_to_select_from(const WhereType& _where = WhereType{},
|
||||
const LimitType& _limit = LimitType{}) {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
using NamedTupleType = rfl::named_tuple_t<std::remove_cvref_t<T>>;
|
||||
using Fields = typename NamedTupleType::Fields;
|
||||
|
||||
const auto columns = make_columns<Fields>(
|
||||
std::make_integer_sequence<int, rfl::tuple_size_v<Fields>>());
|
||||
|
||||
const auto fields = sqlgen::internal::collect::vector(
|
||||
columns | transform([](const auto& _col) -> dynamic::SelectFrom::Field {
|
||||
return dynamic::SelectFrom::Field{.val = _col};
|
||||
}));
|
||||
|
||||
return dynamic::SelectFrom{
|
||||
.table =
|
||||
dynamic::Table{.name = get_tablename<T>(), .schema = get_schema<T>()},
|
||||
.fields = fields,
|
||||
.where = to_condition<std::remove_cvref_t<T>>(_where),
|
||||
.order_by = to_order_by<OrderByType>(),
|
||||
.limit = to_limit(_limit)};
|
||||
}
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_REMOVEAST_HPP_
|
||||
#define SQLGEN_TRANSPILATION_REMOVEAST_HPP_
|
||||
|
||||
#include <rfl.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "As.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T>
|
||||
struct RemoveAs;
|
||||
|
||||
template <class T>
|
||||
struct RemoveAs {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <class T, rfl::internal::StringLiteral _new_name>
|
||||
struct RemoveAs<As<T, _new_name>> {
|
||||
using Type = typename RemoveAs<std::remove_cvref_t<T>>::Type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using remove_as_t = typename RemoveAs<std::remove_cvref_t<T>>::Type;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_REMOVENULLABLET_HPP_
|
||||
#define SQLGEN_TRANSPILATION_REMOVENULLABLET_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#include "is_nullable.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T>
|
||||
struct RemoveNullable;
|
||||
|
||||
template <class T>
|
||||
struct RemoveNullable {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
requires is_ptr<T>::value
|
||||
struct RemoveNullable<T> {
|
||||
using Type =
|
||||
typename RemoveNullable<std::remove_cvref_t<typename T::elem_type>>::Type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct RemoveNullable<std::optional<T>> {
|
||||
using Type = typename RemoveNullable<std::remove_cvref_t<T>>::Type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using remove_nullable_t = typename RemoveNullable<std::remove_cvref_t<T>>::Type;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_TO_GROUP_BY_HPP_
|
||||
#define SQLGEN_TRANSPILATION_TO_GROUP_BY_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../dynamic/Column.hpp"
|
||||
#include "../dynamic/GroupBy.hpp"
|
||||
#include "group_by_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T>
|
||||
struct ToGroupBy;
|
||||
|
||||
template <>
|
||||
struct ToGroupBy<Nothing> {
|
||||
std::optional<dynamic::GroupBy> operator()() const { return std::nullopt; }
|
||||
};
|
||||
|
||||
template <class... ColTypes>
|
||||
struct ToGroupBy<GroupBy<ColTypes...>> {
|
||||
std::optional<dynamic::GroupBy> operator()() const {
|
||||
const auto columns = std::vector<dynamic::Column>(
|
||||
{dynamic::Column{.name = ColTypes().name()}...});
|
||||
return dynamic::GroupBy{.columns = columns};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
const auto to_group_by = ToGroupBy<std::remove_cvref_t<T>>{};
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -9,34 +9,42 @@
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../dynamic/ColumnOrAggregation.hpp"
|
||||
#include "../dynamic/SelectFrom.hpp"
|
||||
#include "../dynamic/Table.hpp"
|
||||
#include "../internal/collect/vector.hpp"
|
||||
#include "check_aggregations.hpp"
|
||||
#include "get_schema.hpp"
|
||||
#include "get_tablename.hpp"
|
||||
#include "make_columns.hpp"
|
||||
#include "make_fields.hpp"
|
||||
#include "to_condition.hpp"
|
||||
#include "to_group_by.hpp"
|
||||
#include "to_limit.hpp"
|
||||
#include "to_order_by.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class T, class WhereType = Nothing, class OrderByType = Nothing,
|
||||
class LimitType = Nothing>
|
||||
requires std::is_class_v<std::remove_cvref_t<T>> &&
|
||||
std::is_aggregate_v<std::remove_cvref_t<T>>
|
||||
dynamic::SelectFrom to_select_from(const WhereType& _where = WhereType{},
|
||||
const LimitType& _limit = LimitType{}) {
|
||||
using NamedTupleType = rfl::named_tuple_t<std::remove_cvref_t<T>>;
|
||||
using Fields = typename NamedTupleType::Fields;
|
||||
template <class StructType, class FieldsType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType>
|
||||
requires std::is_class_v<std::remove_cvref_t<StructType>> &&
|
||||
std::is_aggregate_v<std::remove_cvref_t<StructType>>
|
||||
dynamic::SelectFrom to_select_from(const FieldsType& _fields,
|
||||
const WhereType& _where,
|
||||
const LimitType& _limit) {
|
||||
static_assert(check_aggregations<StructType, FieldsType, GroupByType>(),
|
||||
"The aggregations were not set up correctly. Please check the "
|
||||
"trace for a more detailed error message.");
|
||||
|
||||
const auto columns = make_columns<Fields>(
|
||||
std::make_integer_sequence<int, rfl::tuple_size_v<Fields>>());
|
||||
const auto fields = make_fields<StructType, FieldsType>(
|
||||
_fields,
|
||||
std::make_integer_sequence<int, rfl::tuple_size_v<FieldsType>>());
|
||||
|
||||
return dynamic::SelectFrom{
|
||||
.table =
|
||||
dynamic::Table{.name = get_tablename<T>(), .schema = get_schema<T>()},
|
||||
.columns = columns,
|
||||
.where = to_condition<std::remove_cvref_t<T>>(_where),
|
||||
.table = dynamic::Table{.name = get_tablename<StructType>(),
|
||||
.schema = get_schema<StructType>()},
|
||||
.fields = fields,
|
||||
.where = to_condition<std::remove_cvref_t<StructType>>(_where),
|
||||
.group_by = to_group_by<GroupByType>(),
|
||||
.order_by = to_order_by<OrderByType>(),
|
||||
.limit = to_limit(_limit)};
|
||||
}
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
#include "../dynamic/Statement.hpp"
|
||||
#include "../insert.hpp"
|
||||
#include "../read.hpp"
|
||||
#include "../select_from.hpp"
|
||||
#include "../update.hpp"
|
||||
#include "columns_t.hpp"
|
||||
#include "read_to_select_from.hpp"
|
||||
#include "to_create_index.hpp"
|
||||
#include "to_create_table.hpp"
|
||||
#include "to_delete_from.hpp"
|
||||
@@ -69,8 +71,19 @@ template <class ContainerType, class WhereType, class OrderByType,
|
||||
class LimitType>
|
||||
struct ToSQL<Read<ContainerType, WhereType, OrderByType, LimitType>> {
|
||||
dynamic::Statement operator()(const auto& _read) const {
|
||||
return to_select_from<value_t<ContainerType>, WhereType, OrderByType,
|
||||
LimitType>(_read.where_, _read.limit_);
|
||||
return read_to_select_from<value_t<ContainerType>, WhereType, OrderByType,
|
||||
LimitType>(_read.where_, _read.limit_);
|
||||
}
|
||||
};
|
||||
|
||||
template <class StructType, class FieldsType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType>
|
||||
struct ToSQL<SelectFrom<StructType, FieldsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType>> {
|
||||
dynamic::Statement operator()(const auto& _select_from) const {
|
||||
return to_select_from<StructType, FieldsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType>(
|
||||
_select_from.fields_, _select_from.where_, _select_from.limit_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "create_index.hpp"
|
||||
#include "delete_from.hpp"
|
||||
#include "read.hpp"
|
||||
#include "select_from.hpp"
|
||||
#include "transpilation/Limit.hpp"
|
||||
#include "transpilation/value_t.hpp"
|
||||
#include "update.hpp"
|
||||
@@ -55,6 +56,27 @@ auto operator|(const Read<ContainerType, WhereType, OrderByType, LimitType>& _r,
|
||||
.where_ = _where.condition};
|
||||
}
|
||||
|
||||
template <class StructType, class FieldsTupleType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType, class ToType,
|
||||
class ConditionType>
|
||||
auto operator|(
|
||||
const SelectFrom<StructType, FieldsTupleType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, ToType>& _s,
|
||||
const Where<ConditionType>& _where) {
|
||||
static_assert(std::is_same_v<WhereType, Nothing>,
|
||||
"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 cannot call order_by(...) before where(...).");
|
||||
static_assert(std::is_same_v<LimitType, Nothing>,
|
||||
"You cannot call limit(...) before where(...).");
|
||||
static_assert(std::is_same_v<ToType, Nothing>,
|
||||
"You cannot call to<...> before where(...).");
|
||||
return SelectFrom<StructType, FieldsTupleType, ConditionType, GroupByType,
|
||||
OrderByType, LimitType, ToType>{.fields_ = _s.fields_,
|
||||
.where_ = _where.condition};
|
||||
}
|
||||
|
||||
template <class ValueType, class SetsType, class WhereType, class ConditionType>
|
||||
auto operator|(const Update<ValueType, SetsType, WhereType>& _u,
|
||||
const Where<ConditionType>& _where) {
|
||||
|
||||
Reference in New Issue
Block a user