mirror of
https://github.com/getml/sqlgen.git
synced 2025-12-31 06:30:18 -06:00
Make sure we properly check the columns in the ORDER BY clause when there is a GROUP BY (#27)
This commit is contained in:
committed by
GitHub
parent
1170f22d69
commit
e443cb56e0
@@ -113,7 +113,7 @@ struct Read {
|
||||
"You must assign at least one column to order by(...).");
|
||||
return Read<Type, WhereType,
|
||||
transpilation::order_by_t<
|
||||
transpilation::value_t<Type>,
|
||||
transpilation::value_t<Type>, Nothing,
|
||||
typename std::remove_cvref_t<ColTypes>::ColType...>,
|
||||
LimitType>{.where_ = _r.where_};
|
||||
}
|
||||
|
||||
@@ -206,11 +206,16 @@ struct SelectFrom {
|
||||
"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, AliasType, FieldsType, JoinsType, WhereType, GroupByType,
|
||||
transpilation::order_by_t<
|
||||
StructType, typename std::remove_cvref_t<ColTypes>::ColType...>,
|
||||
LimitType, ToType>{
|
||||
|
||||
using TableTupleType =
|
||||
transpilation::table_tuple_t<StructType, AliasType, JoinsType>;
|
||||
|
||||
using NewOrderByType = transpilation::order_by_t<
|
||||
TableTupleType, GroupByType,
|
||||
typename std::remove_cvref_t<ColTypes>::ColType...>;
|
||||
|
||||
return SelectFrom<StructType, AliasType, FieldsType, JoinsType, WhereType,
|
||||
GroupByType, NewOrderByType, LimitType, ToType>{
|
||||
.fields_ = _s.fields_, .joins_ = _s.joins_, .where_ = _s.where_};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,24 +12,24 @@
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class StructType, class FieldsType, class GroupByType>
|
||||
template <class TablesType, 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> {
|
||||
template <class TablesType, class... FieldTypes>
|
||||
requires(true && ... && !MakeField<TablesType, FieldTypes>::is_aggregation)
|
||||
struct CheckAggregation<TablesType, 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> {
|
||||
template <class TablesType, class... FieldTypes>
|
||||
requires(false || ... || MakeField<TablesType, FieldTypes>::is_aggregation)
|
||||
struct CheckAggregation<TablesType, rfl::Tuple<FieldTypes...>, Nothing> {
|
||||
static constexpr bool value =
|
||||
(true && ... &&
|
||||
(MakeField<StructType, FieldTypes>::is_aggregation ||
|
||||
!MakeField<StructType, FieldTypes>::is_column));
|
||||
(MakeField<TablesType, FieldTypes>::is_aggregation ||
|
||||
!MakeField<TablesType, FieldTypes>::is_column));
|
||||
static_assert(
|
||||
value,
|
||||
"If any column is aggregated and there is no GROUP BY, then all columns "
|
||||
@@ -37,8 +37,8 @@ struct CheckAggregation<StructType, rfl::Tuple<FieldTypes...>, Nothing> {
|
||||
};
|
||||
|
||||
/// Case: There is a group by.
|
||||
template <class StructType, class... FieldTypes, class... ColTypes>
|
||||
struct CheckAggregation<StructType, rfl::Tuple<FieldTypes...>,
|
||||
template <class TablesType, class... FieldTypes, class... ColTypes>
|
||||
struct CheckAggregation<TablesType, rfl::Tuple<FieldTypes...>,
|
||||
GroupBy<ColTypes...>> {
|
||||
template <class F>
|
||||
static constexpr bool included_in_group_by =
|
||||
@@ -48,9 +48,9 @@ struct CheckAggregation<StructType, rfl::Tuple<FieldTypes...>,
|
||||
|
||||
static constexpr bool value =
|
||||
(true && ... &&
|
||||
(MakeField<StructType, FieldTypes>::is_aggregation ||
|
||||
(!MakeField<StructType, FieldTypes>::is_column &&
|
||||
!MakeField<StructType, FieldTypes>::is_operation) ||
|
||||
(MakeField<TablesType, FieldTypes>::is_aggregation ||
|
||||
(!MakeField<TablesType, FieldTypes>::is_column &&
|
||||
!MakeField<TablesType, FieldTypes>::is_operation) ||
|
||||
included_in_group_by<FieldTypes>));
|
||||
|
||||
static_assert(value,
|
||||
@@ -58,9 +58,9 @@ struct CheckAggregation<StructType, rfl::Tuple<FieldTypes...>,
|
||||
"must either be aggregated or included inside the GROUP BY.");
|
||||
};
|
||||
|
||||
template <class StructType, class FieldsType, class GroupByType>
|
||||
template <class TablesType, class FieldsType, class GroupByType>
|
||||
consteval bool check_aggregations() {
|
||||
return CheckAggregation<std::remove_cvref_t<StructType>,
|
||||
return CheckAggregation<std::remove_cvref_t<TablesType>,
|
||||
std::remove_cvref_t<FieldsType>,
|
||||
std::remove_cvref_t<GroupByType>>::value;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Col.hpp"
|
||||
#include "Desc.hpp"
|
||||
#include "all_columns_exist.hpp"
|
||||
#include "check_aggregations.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
@@ -31,17 +32,26 @@ struct OrderByWrapper<Desc<transpilation::Col<_name, _alias>>> {
|
||||
template <class... WrapperTypes>
|
||||
struct OrderBy {};
|
||||
|
||||
template <class T, class... ColTypes>
|
||||
template <class TablesType, class GroupByType, class... ColTypes>
|
||||
auto make_order_by() {
|
||||
static_assert(
|
||||
all_columns_exist<T, typename OrderByWrapper<ColTypes>::ColType...>(),
|
||||
all_columns_exist<TablesType,
|
||||
typename OrderByWrapper<ColTypes>::ColType...>(),
|
||||
"A column in order_by does not exist.");
|
||||
static_assert(
|
||||
check_aggregations<
|
||||
TablesType, rfl::Tuple<typename OrderByWrapper<ColTypes>::ColType...>,
|
||||
GroupByType>(),
|
||||
"The columns in the ORDER BY clause have not been properly "
|
||||
"aggregated. Please refer to the stack trace for details.");
|
||||
|
||||
return OrderBy<OrderByWrapper<ColTypes>...>{};
|
||||
}
|
||||
|
||||
template <class T, class... ColTypes>
|
||||
template <class TablesType, class GroupByType, class... ColTypes>
|
||||
using order_by_t = std::invoke_result_t<
|
||||
decltype(make_order_by<T, typename ColTypes::ColType...>)>;
|
||||
decltype(make_order_by<TablesType, GroupByType,
|
||||
typename ColTypes::ColType...>)>;
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ struct Relationship {
|
||||
sqlgen::PrimaryKey<uint32_t> child_id;
|
||||
};
|
||||
|
||||
TEST(postgres, test_joins_nested) {
|
||||
TEST(postgres, test_joins_nested_grouped) {
|
||||
const auto people1 = std::vector<Person>(
|
||||
{Person{
|
||||
.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
|
||||
|
||||
Reference in New Issue
Block a user