Added aggregations and GROUP BY (#17)

This commit is contained in:
Dr. Patrick Urbanke (劉自成)
2025-06-08 12:30:59 +02:00
committed by GitHub
parent 21564cacf6
commit 775f15babf
53 changed files with 2276 additions and 48 deletions
+5
View File
@@ -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"
+54
View File
@@ -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
+40
View File
@@ -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
+7
View File
@@ -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>>{};
+45
View File
@@ -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
+16
View File
@@ -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
+11 -1
View File
@@ -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;
};
+49
View File
@@ -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
+28
View File
@@ -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
+10 -5
View File
@@ -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
+17 -4
View File
@@ -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}; };
+25 -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>,
"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...>{};
+3 -5
View File
@@ -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_;
};
+126
View File
@@ -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
+33
View File
@@ -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
+20
View File
@@ -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
+1 -1
View File
@@ -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
+204
View File
@@ -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
+1 -1
View File
@@ -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
+23 -15
View File
@@ -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)};
}
+15 -2
View File
@@ -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_);
}
};
+22
View File
@@ -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) {