query/select: add DISTINCT clauses

This commit is contained in:
Danny Robson 2023-08-04 12:39:11 +10:00
parent 362ad5d563
commit 365628ddd3
3 changed files with 106 additions and 9 deletions

View File

@ -17,7 +17,8 @@ template <
typename ExpressionT,
typename FromT,
typename WhereT,
typename GroupT
typename GroupT,
typename DistinctT
>
struct select_t;

View File

@ -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).
//

View File

@ -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 ();
}