query/select: add DISTINCT clauses
This commit is contained in:
parent
362ad5d563
commit
365628ddd3
|
@ -17,7 +17,8 @@ template <
|
|||
typename ExpressionT,
|
||||
typename FromT,
|
||||
typename WhereT,
|
||||
typename GroupT
|
||||
typename GroupT,
|
||||
typename DistinctT
|
||||
>
|
||||
struct select_t;
|
||||
|
||||
|
|
|
@ -83,15 +83,25 @@ using select_output_t = typename select_output<T>::type;
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Encodes a select statement.
|
||||
///
|
||||
/// \tparam ExpressionT
|
||||
/// \tparam FromT
|
||||
/// \tparam WhereT
|
||||
/// \tparam GroupT
|
||||
/// \tparam DistinctT Represents "DISTINCT" clauses.
|
||||
/// * nullptr_t if none
|
||||
/// * std::tuple<> if "DISTINCT"
|
||||
/// * std::tuple<...> if "DISTINCT ON".
|
||||
template <
|
||||
typename ExpressionT = std::tuple<>,
|
||||
typename FromT = std::tuple<>,
|
||||
typename WhereT = std::tuple<>,
|
||||
typename GroupT = std::tuple<>
|
||||
typename GroupT = std::tuple<>,
|
||||
typename DistinctT = nullptr_t
|
||||
>
|
||||
struct select_t : std::conditional_t<
|
||||
std::tuple_size_v<ExpressionT> == 1,
|
||||
expression_t<select_t<ExpressionT, FromT, WhereT, GroupT>>,
|
||||
expression_t<select_t<ExpressionT, FromT, WhereT, GroupT, DistinctT>>,
|
||||
std::identity
|
||||
> {
|
||||
using expression_type = ExpressionT;
|
||||
|
@ -116,6 +126,9 @@ struct select_t : std::conditional_t<
|
|||
static_assert (is_tuple_v<where_type>);
|
||||
group_type group_;
|
||||
|
||||
using distinct_type = DistinctT;
|
||||
distinct_type distinct_;
|
||||
|
||||
/// A helper that constructs a new select_t object from members-as-parameters.
|
||||
template <typename ...T>
|
||||
auto
|
||||
|
@ -136,7 +149,8 @@ struct select_t : std::conditional_t<
|
|||
expressions_,
|
||||
combine (from_, newfrom),
|
||||
where_,
|
||||
group_
|
||||
group_,
|
||||
distinct_
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -150,7 +164,8 @@ struct select_t : std::conditional_t<
|
|||
expressions_,
|
||||
combine (from_, join_t { a, b, condition }),
|
||||
where_,
|
||||
group_
|
||||
group_,
|
||||
distinct_
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -164,7 +179,8 @@ struct select_t : std::conditional_t<
|
|||
expressions_,
|
||||
from_,
|
||||
combine (where_, condition),
|
||||
group_
|
||||
group_,
|
||||
distinct_
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -178,9 +194,52 @@ struct select_t : std::conditional_t<
|
|||
expressions_,
|
||||
from_,
|
||||
where_,
|
||||
combine (group_, condition)
|
||||
combine (group_, condition),
|
||||
distinct_
|
||||
);
|
||||
}
|
||||
|
||||
auto
|
||||
distinct [[nodiscard]] (void)
|
||||
requires (std::is_same_v<distinct_type, nullptr_t>)
|
||||
{
|
||||
return make (
|
||||
expressions_,
|
||||
from_,
|
||||
where_,
|
||||
group_,
|
||||
std::tuple<> {}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// Constructs a new select statement with the provided expression
|
||||
/// appended to the current list of group_by conditions
|
||||
template <typename ...OnT>
|
||||
auto
|
||||
distinct [[nodiscard]] (OnT &&...on)
|
||||
{
|
||||
if constexpr (std::is_same_v<distinct_type, nullptr_t>) {
|
||||
return make (
|
||||
expressions_,
|
||||
from_,
|
||||
where_,
|
||||
group_,
|
||||
std::make_tuple (std::forward<OnT> (on)...)
|
||||
);
|
||||
} else {
|
||||
return make (
|
||||
expressions_,
|
||||
from_,
|
||||
where_,
|
||||
group_,
|
||||
std::tuple_cat (
|
||||
distinct_,
|
||||
std::make_tuple (std::forward<OnT> (on)...)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -211,6 +270,7 @@ select (ExpressionT &&...expr)
|
|||
std::tuple<> {},
|
||||
std::tuple<> {},
|
||||
std::tuple<> {},
|
||||
nullptr
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -335,8 +395,20 @@ struct fmt::formatter<select_t<T...>> {
|
|||
static_assert ((!has_cvref_v<T> && ...));
|
||||
using select_type = select_t<T...>;
|
||||
|
||||
cursor = fmt::format_to (cursor, "SELECT");
|
||||
|
||||
// Distinct
|
||||
if constexpr (!std::is_same_v<typename select_type::distinct_type, nullptr_t>) {
|
||||
if constexpr (std::tuple_size_v<typename select_type::distinct_type> == 0) {
|
||||
cursor = fmt::format_to (cursor, " DISTINCT");
|
||||
} else if constexpr (std::tuple_size_v<typename select_type ::distinct_type>) {
|
||||
cursor = fmt::format_to (cursor, " DISTINCT ON ({})", fmt::join (val.distinct_, ", "));
|
||||
}
|
||||
}
|
||||
|
||||
// Expressions
|
||||
static_assert (is_tuple_v<typename select_type::expression_type>);
|
||||
cursor = fmt::format_to (cursor, "SELECT {}", fmt::join (val.expressions_, ", "));
|
||||
cursor = fmt::format_to (cursor, " {}", fmt::join (val.expressions_, ", "));
|
||||
|
||||
// The 'FROM' case is a touch ugly to preserve ease-of-use (from the consumer side).
|
||||
//
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
#include <cruft/db/query/select.hpp>
|
||||
#include <cruft/db/query/literal.hpp>
|
||||
#include <cruft/db/query/expression.hpp>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
|
||||
void test_literals (cruft::TAP::logger &tap)
|
||||
void
|
||||
test_literals (cruft::TAP::logger &tap)
|
||||
{
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
|
@ -26,10 +28,32 @@ void test_literals (cruft::TAP::logger &tap)
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
test_distinct (cruft::TAP::logger &tap)
|
||||
{
|
||||
{
|
||||
auto const q = select (comment_.c.author_id).distinct ();
|
||||
auto const sql = fmt::format ("{};", q);
|
||||
tap.expect_eq (sql, "SELECT DISTINCT comment.author_id FROM comment;", "select distinct");
|
||||
}
|
||||
|
||||
{
|
||||
auto const q = select (comment_.c.body).distinct (comment_.c.author_id);
|
||||
auto const sql = fmt::format ("{};", q);
|
||||
tap.expect_eq (
|
||||
sql,
|
||||
"SELECT DISTINCT ON (comment.author_id) comment.body FROM comment;",
|
||||
"select distinct on"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
cruft::TAP::logger tap;
|
||||
|
||||
test_literals (tap);
|
||||
test_distinct (tap);
|
||||
|
||||
return tap.status ();
|
||||
}
|
Loading…
Reference in New Issue