diff --git a/include/sqlpp23/core/clause/union_order_by.h b/include/sqlpp23/core/clause/union_order_by.h index a6501ed1..9f67d86c 100644 --- a/include/sqlpp23/core/clause/union_order_by.h +++ b/include/sqlpp23/core/clause/union_order_by.h @@ -67,7 +67,7 @@ struct simple_sort_order_expression { private: friend reader_t; simple_column_t _lhs; - sort_type _rhs; + sort_order _rhs; }; template diff --git a/include/sqlpp23/core/operator/comparison_functions.h b/include/sqlpp23/core/operator/comparison_functions.h index 7481522a..6d58b981 100644 --- a/include/sqlpp23/core/operator/comparison_functions.h +++ b/include/sqlpp23/core/operator/comparison_functions.h @@ -169,19 +169,42 @@ enum class sort_type { template requires(values_are_comparable::value) constexpr auto asc(L l) -> sort_order_expression { - return {l, sort_type::asc}; + return {std::move(l), {sort_type::asc, std::nullopt}}; } template requires(values_are_comparable::value) constexpr auto desc(L l) -> sort_order_expression { - return {l, sort_type::desc}; + return {std::move(l), {sort_type::desc, std::nullopt}}; } template requires(values_are_comparable::value) constexpr auto order(L l, sort_type order) -> sort_order_expression { - return {l, order}; + return {std::move(l), {std::move(order), std::nullopt}}; +} + +enum class nulls_pos { + first, + last, +}; + +template + requires(values_are_comparable::value) +constexpr auto nulls_first(L l) -> sort_order_expression { + return {std::move(l), {std::nullopt , nulls_pos::first}}; +} + +template + requires(values_are_comparable::value) +constexpr auto nulls_last(L l) -> sort_order_expression { + return {std::move(l), {std::nullopt , nulls_pos::last}}; +} + +template + requires(values_are_comparable::value) +constexpr auto nulls_order(L l, nulls_pos pos) -> sort_order_expression { + return {std::move(l), {std::nullopt , std::move(pos)}}; } } // namespace sqlpp diff --git a/include/sqlpp23/core/operator/enable_comparison.h b/include/sqlpp23/core/operator/enable_comparison.h index 426eee4c..dcf78535 100644 --- a/include/sqlpp23/core/operator/enable_comparison.h +++ b/include/sqlpp23/core/operator/enable_comparison.h @@ -125,6 +125,24 @@ class enable_comparison { return ::sqlpp::order(std::forward(self), t); } + template + constexpr auto nulls_first(this Expr&& self) + -> decltype(::sqlpp::nulls_first(std::forward(self))) { + return ::sqlpp::nulls_first(std::forward(self)); + } + + template + constexpr auto nulls_last(this Expr&& self) + -> decltype(::sqlpp::nulls_last(std::forward(self))) { + return ::sqlpp::nulls_last(std::forward(self)); + } + + template + constexpr auto nulls_order(this Expr&& self, ::sqlpp::nulls_pos t) + -> decltype(::sqlpp::nulls_order(std::forward(self), t)) { + return ::sqlpp::nulls_order(std::forward(self), t); + } + template constexpr auto like(this Expr&& self, R r) -> decltype(::sqlpp::like(std::forward(self), std::move(r))) { diff --git a/include/sqlpp23/core/operator/sort_order_expression.h b/include/sqlpp23/core/operator/sort_order_expression.h index 9f41ab5d..b7d22733 100644 --- a/include/sqlpp23/core/operator/sort_order_expression.h +++ b/include/sqlpp23/core/operator/sort_order_expression.h @@ -32,9 +32,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include namespace sqlpp { + +struct sort_order { + constexpr sort_order(std::optional l = std::nullopt, std::optional r = std::nullopt) + : _lhs(std::move(l)), _rhs(std::move(r)) {} + sort_order(const sort_order&) = default; + sort_order(sort_order&&) = default; + sort_order& operator=(const sort_order&) = default; + sort_order& operator=(sort_order&&) = default; + ~sort_order() = default; + + std::optional _lhs; + std::optional _rhs; +}; + template struct sort_order_expression { - constexpr sort_order_expression(L l, sort_type r) + constexpr sort_order_expression(L l, sort_order r) : _lhs(std::move(l)), _rhs(std::move(r)) {} sort_order_expression(const sort_order_expression&) = default; sort_order_expression(sort_order_expression&&) = default; @@ -42,12 +56,34 @@ struct sort_order_expression { sort_order_expression& operator=(sort_order_expression&&) = default; ~sort_order_expression() = default; + constexpr auto nulls_first() -> sort_order_expression; + constexpr auto nulls_last() -> sort_order_expression; + constexpr auto nulls_order(::sqlpp::nulls_pos t) -> sort_order_expression; + private: friend reader_t; - L _lhs; - sort_type _rhs; + const L _lhs; + const sort_order _rhs; +}; + +template +constexpr auto sort_order_expression::nulls_first() -> sort_order_expression { + static_assert(_rhs._rhs.has_value(), "nulls_first() can only be called when nulls_pos is not already set."); + return {std::move(_lhs), {std::move(_rhs._lhs), ::sqlpp::nulls_pos::first}}; +} + +template +constexpr auto sort_order_expression::nulls_last() -> sort_order_expression { + static_assert(_rhs._rhs.has_value(), "nulls_last() can only be called when nulls_pos is not already set."); + return {std::move(_lhs), {std::move(_rhs._lhs), ::sqlpp::nulls_pos::last}}; }; +template +constexpr auto sort_order_expression::nulls_order(::sqlpp::nulls_pos t) -> sort_order_expression { + static_assert(_rhs._rhs.has_value(), "nulls_order(::sqlpp::nulls_pos t) can only be called when nulls_pos is not already set."); + return {std::move(_lhs), {std::move(_rhs._lhs), std::move(t)}}; +} + template struct nodes_of> { using type = detail::type_vector; @@ -64,6 +100,20 @@ auto to_sql_string(Context&, const sort_type& t) -> std::string { return " DESC"; } +template +auto to_sql_string(Context&, const nulls_pos& t) -> std::string { + if (t == nulls_pos::first) { + return " NULLS FIRST"; + } + return " NULLS LAST"; +} + +template +auto to_sql_string(Context& context, const sort_order& t) + -> std::string { + return to_sql_string(context, read.lhs(t)) + to_sql_string(context, read.rhs(t)); +} + template auto to_sql_string(Context& context, const sort_order_expression& t) -> std::string { diff --git a/tests/core/serialize/operator/sort_order_expression.cpp b/tests/core/serialize/operator/sort_order_expression.cpp new file mode 100644 index 00000000..adc118aa --- /dev/null +++ b/tests/core/serialize/operator/sort_order_expression.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +int main(int, char*[]) { + const auto val = sqlpp::value(1); + const auto expr = sqlpp::value(17) + 4; + + // Operands are enclosed in parentheses where required. + SQLPP_COMPARE(val.nulls_last(), "1 NULLS LAST"); + SQLPP_COMPARE(val.nulls_order(sqlpp::nulls_pos::first), "1 NULLS FIRST"); + SQLPP_COMPARE(val.nulls_order(sqlpp::nulls_pos::last), "1 NULLS LAST"); + SQLPP_COMPARE(val.nulls_first(), "1 NULLS FIRST"); + SQLPP_COMPARE(val.asc(), "1 ASC"); + SQLPP_COMPARE(val.asc().nulls_first(), "1 ASC NULLS FIRST"); + SQLPP_COMPARE(val.asc().nulls_last(), "1 ASC NULLS LAST"); + SQLPP_COMPARE(val.asc().nulls_order(sqlpp::nulls_pos::first), "1 ASC NULLS FIRST"); + SQLPP_COMPARE(val.asc().nulls_order(sqlpp::nulls_pos::last), "1 ASC NULLS LAST"); + SQLPP_COMPARE(val.desc(), "1 DESC"); + SQLPP_COMPARE(val.desc().nulls_first(), "1 DESC NULLS FIRST"); + SQLPP_COMPARE(val.desc().nulls_last(), "1 DESC NULLS LAST"); + SQLPP_COMPARE(val.desc().nulls_order(sqlpp::nulls_pos::first), "1 DESC NULLS FIRST"); + SQLPP_COMPARE(val.desc().nulls_order(sqlpp::nulls_pos::last), "1 DESC NULLS LAST"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::asc), "1 ASC"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::asc).nulls_first().nulls_first(), "1 ASC NULLS FIRST"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::asc).nulls_last(), "1 ASC NULLS LAST"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::asc).nulls_order(sqlpp::nulls_pos::first), "1 ASC NULLS FIRST"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::asc).nulls_order(sqlpp::nulls_pos::last), "1 ASC NULLS LAST"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::desc), "1 DESC"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::desc).nulls_first(), "1 DESC NULLS FIRST"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::desc).nulls_last(), "1 DESC NULLS LAST"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::desc).nulls_order(sqlpp::nulls_pos::first), "1 DESC NULLS FIRST"); + SQLPP_COMPARE(val.order(sqlpp::sort_type::desc).nulls_order(sqlpp::nulls_pos::last), "1 DESC NULLS LAST"); + + SQLPP_COMPARE(expr.nulls_first(), "(17 + 4) NULLS FIRST"); + SQLPP_COMPARE(expr.nulls_last(), "(17 + 4) NULLS LAST"); + SQLPP_COMPARE(expr.nulls_order(sqlpp::nulls_pos::first), "(17 + 4) NULLS FIRST"); + SQLPP_COMPARE(expr.nulls_order(sqlpp::nulls_pos::last), "(17 + 4) NULLS LAST"); + SQLPP_COMPARE(expr.asc(), "(17 + 4) ASC"); + SQLPP_COMPARE(expr.asc().nulls_first(), "(17 + 4) ASC NULLS FIRST"); + SQLPP_COMPARE(expr.asc().nulls_last(), "(17 + 4) ASC NULLS LAST"); + SQLPP_COMPARE(expr.asc().nulls_order(sqlpp::nulls_pos::first), "(17 + 4) ASC NULLS FIRST"); + SQLPP_COMPARE(expr.asc().nulls_order(sqlpp::nulls_pos::last), "(17 + 4) ASC NULLS LAST"); + SQLPP_COMPARE(expr.desc(), "(17 + 4) DESC"); + SQLPP_COMPARE(expr.desc().nulls_first(), "(17 + 4) DESC NULLS FIRST"); + SQLPP_COMPARE(expr.desc().nulls_last(), "(17 + 4) DESC NULLS LAST"); + SQLPP_COMPARE(expr.desc().nulls_order(sqlpp::nulls_pos::first), "(17 + 4) DESC NULLS FIRST"); + SQLPP_COMPARE(expr.desc().nulls_order(sqlpp::nulls_pos::last), "(17 + 4) DESC NULLS LAST"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::asc), "(17 + 4) ASC"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::asc).nulls_first(), "(17 + 4) ASC NULLS FIRST"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::asc).nulls_last(), "(17 + 4) ASC NULLS LAST"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::asc).nulls_order(sqlpp::nulls_pos::first), "(17 + 4) ASC NULLS FIRST"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::asc).nulls_order(sqlpp::nulls_pos::last), "(17 + 4) ASC NULLS LAST"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::desc), "(17 + 4) DESC"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::desc).nulls_first(), "(17 + 4) DESC NULLS FIRST"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::desc).nulls_last(), "(17 + 4) DESC NULLS LAST"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::desc).nulls_order(sqlpp::nulls_pos::first), "(17 + 4) DESC NULLS FIRST"); + SQLPP_COMPARE(expr.order(sqlpp::sort_type::desc).nulls_order(sqlpp::nulls_pos::last), "(17 + 4) DESC NULLS LAST"); + + return 0; +}