9#include <unordered_set>
13#include "orm-cxx/model/ColumnType.hpp"
14#include "orm-cxx/model/Mapping.hpp"
15#include "orm-cxx/query/SelectSpec.hpp"
16#include "orm-cxx/reflection/Reflection.hpp"
21template <
typename SchemaType>
26inline auto isSupportedProjectionResultType(model::ColumnType type) ->
bool
30 case model::ColumnType::Bool:
31 case model::ColumnType::Char:
32 case model::ColumnType::UnsignedChar:
33 case model::ColumnType::Short:
34 case model::ColumnType::UnsignedShort:
35 case model::ColumnType::Int:
36 case model::ColumnType::UnsignedInt:
37 case model::ColumnType::LongLong:
38 case model::ColumnType::UnsignedLongLong:
39 case model::ColumnType::Float:
40 case model::ColumnType::Double:
41 case model::ColumnType::String:
43 case model::ColumnType::Uuid:
53 std::optional<model::ColumnType> type;
57consteval auto projectionLogicalType() -> std::optional<model::ColumnType>
59 using field_t = std::remove_cvref_t<T>;
60 using value_t = std::remove_cv_t<model::detail::static_optional_value_t<field_t>>;
61 if constexpr (
requires { model::LogicalTypeTraits<value_t>::value; })
63 return model::LogicalTypeTraits<value_t>::value;
71inline auto validateProjectionAliasNames(
const std::vector<query::Projection>& projections,
72 const std::vector<ProjectionResultField>& fields) ->
void
74 if (projections.empty())
76 throw std::invalid_argument{
"Projection query requires at least one projected field"};
79 std::unordered_set<std::string> resultFields;
81 for (
const auto& field : fields)
83 if (not field.type.has_value() or not isSupportedProjectionResultType(field.type.value()))
85 throw std::invalid_argument{
"Unsupported projection result field type: " + field.name};
88 resultFields.insert(field.name);
91 std::unordered_set<std::string> projectedFields;
93 for (
const auto& projection : projections)
95 if (projection.resultField.empty())
97 throw std::invalid_argument{
"Projection alias must not be empty"};
100 if (not resultFields.contains(projection.resultField))
102 throw std::invalid_argument{
"Projection alias does not match a result field: " + projection.resultField};
105 if (not projectedFields.insert(projection.resultField).second)
107 throw std::invalid_argument{
"Duplicate projection alias: " + projection.resultField};
111 for (
const auto& resultField : resultFields)
113 if (not projectedFields.contains(resultField))
115 throw std::invalid_argument{
"Missing projection alias for result field: " + resultField};
120template <
typename Result>
121auto validateProjectionAliases(
const std::vector<query::Projection>& projections) ->
void
123 std::vector<ProjectionResultField> resultFields;
124 resultFields.reserve(reflection::fieldCount<Result>);
126 [&]<std::size_t... Indices>(std::index_sequence<Indices...>)
128 constexpr auto fields = reflection::fields<Result>();
129 auto appendField = [&]<std::size_t Index>()
131 constexpr auto type = projectionLogicalType<reflection::field_type_t<Result, Index>>();
132 resultFields.emplace_back(std::string{fields[Index].name}, type);
134 (appendField.template operator()<Indices>(), ...);
135 }(std::make_index_sequence<reflection::fieldCount<Result>>{});
137 validateProjectionAliasNames(projections, resultFields);
147template <
typename Source,
typename Result>
151 ProjectionQuery() =
default;
153 template <
typename... Projections>
154 auto project(Projections... projections) -> ProjectionQuery<Source, Result>&
156 data.projections = {std::move(projections)...};
157 detail::validateProjectionAliases<Result>(data.projections);
162 auto where(
const query::Predicate& predicate) -> ProjectionQuery<Source, Result>&
164 data.predicate = predicate;
169 auto andWhere(
const query::Predicate& predicate) -> ProjectionQuery<Source, Result>&
171 data.predicate = data.predicate.has_value() ? data.predicate.value() && predicate : predicate;
176 auto orWhere(
const query::Predicate& predicate) -> ProjectionQuery<Source, Result>&
178 data.predicate = data.predicate.has_value() ? data.predicate.value() || predicate : predicate;
183 template <
typename... Orders>
184 auto orderBy(Orders... orders) -> ProjectionQuery<Source, Result>&
186 data.orderBy = {std::move(orders)...};
191 template <
typename... Columns>
192 auto groupBy(Columns... columns) -> ProjectionQuery<Source, Result>&
194 data.groupBy = {std::move(columns)...};
199 auto having(
const query::AggregatePredicate& predicate) -> ProjectionQuery<Source, Result>&
201 data.having = predicate;
206 auto andHaving(
const query::AggregatePredicate& predicate) -> ProjectionQuery<Source, Result>&
208 data.having = data.having.has_value() ? data.having.value() && predicate : predicate;
213 auto orHaving(
const query::AggregatePredicate& predicate) -> ProjectionQuery<Source, Result>&
215 data.having = data.having.has_value() ? data.having.value() || predicate : predicate;
220 inline auto distinct() -> ProjectionQuery<Source, Result>&
222 data.isDistinct =
true;
227 inline auto offset(std::size_t offset) -> ProjectionQuery<Source, Result>&
229 data.offset = offset;
234 inline auto limit(std::size_t limit) -> ProjectionQuery<Source, Result>&
241 inline auto disableJoining() -> ProjectionQuery<Source, Result>&
243 data.shouldJoin =
false;
253 [[nodiscard]]
inline auto getData()
const ->
const query::SelectSpec&
255 detail::validateProjectionAliases<Result>(data.projections);
260 query::SelectSpec data;
A class representing a database in the ORM framework.
Definition database.hpp:67
Database facade bound to one closed compile-time model schema.
Definition database.hpp:704
Definition projection_query.hpp:51