0
0
mirror of https://github.com/rbock/sqlpp11.git synced 2024-11-16 04:47:18 +08:00

More tests and cleaner detection for result clause

(that is the clause that determines the result type of the statement)
This commit is contained in:
Roland Bock 2024-08-17 12:35:06 +02:00
parent 40d03f5312
commit b95a23b161
17 changed files with 187 additions and 20 deletions

View File

@ -43,7 +43,7 @@ namespace sqlpp
struct insert_t : public statement_name_t<insert_name_t> struct insert_t : public statement_name_t<insert_name_t>
{ {
using _traits = make_traits<no_value_t, tag::is_return_value>; using _traits = make_traits<no_value_t>;
struct _sqlpp_name_tag struct _sqlpp_name_tag
{ {
}; };
@ -86,6 +86,9 @@ namespace sqlpp
}; };
}; };
template<>
struct is_result_clause<insert_t> : public std::true_type {};
template <typename Context> template <typename Context>
auto to_sql_string(Context& , const insert_name_t&) -> std::string auto to_sql_string(Context& , const insert_name_t&) -> std::string
{ {

View File

@ -42,7 +42,7 @@ namespace sqlpp
}; };
struct remove_t : public statement_name_t<remove_name_t> struct remove_t : public statement_name_t<remove_name_t>
{ {
using _traits = make_traits<no_value_t, tag::is_return_value>; using _traits = make_traits<no_value_t>;
struct _sqlpp_name_tag struct _sqlpp_name_tag
{ {
}; };
@ -85,6 +85,9 @@ namespace sqlpp
}; };
}; };
template<>
struct is_result_clause<remove_t> : public std::true_type {};
template <typename Context> template <typename Context>
auto to_sql_string(Context& , const remove_name_t&) -> std::string auto to_sql_string(Context& , const remove_name_t&) -> std::string
{ {

View File

@ -67,7 +67,7 @@ namespace sqlpp
template <typename... Columns> template <typename... Columns>
struct select_traits struct select_traits
{ {
using _traits = make_traits<no_value_t, tag::is_select_column_list, tag::is_return_value>; using _traits = make_traits<no_value_t, tag::is_select_column_list>;
}; };
template <typename Column> template <typename Column>
@ -75,7 +75,6 @@ namespace sqlpp
{ {
using _traits = make_traits<value_type_of_t<Column>, using _traits = make_traits<value_type_of_t<Column>,
tag::is_select_column_list, tag::is_select_column_list,
tag::is_return_value,
tag::is_expression, tag::is_expression,
tag::is_selectable>; tag::is_selectable>;
}; };
@ -227,6 +226,24 @@ namespace sqlpp
{ {
}; };
// If a GROUP BY clause defines known aggregate columns or the SELECT columns contain an aggregate function then ALL
// columns need to be aggregate.
template <typename KnownAggregateColumns, typename... Columns>
struct has_correct_aggregates<KnownAggregateColumns, select_column_list_t<Columns...>>
: public std::integral_constant<
bool,
(detail::type_vector_size<KnownAggregateColumns>::value == 0 and
logic::none_t<contains_aggregate_function<remove_dynamic_t<remove_as_t<Columns>>>::value...>::value) or
logic::all_t<is_aggregate_expression<KnownAggregateColumns,
remove_dynamic_t<remove_as_t<Columns>>>::value...>::value>
{
};
template <typename... Columns>
struct is_result_clause<select_column_list_t<Columns...>> : public std::true_type
{
};
template <typename... Columns> template <typename... Columns>
struct nodes_of<select_column_list_t<Columns...>> struct nodes_of<select_column_list_t<Columns...>>
{ {

View File

@ -60,7 +60,7 @@ namespace sqlpp
template <typename Flag, typename Lhs, typename Rhs> template <typename Flag, typename Lhs, typename Rhs>
struct union_t struct union_t
{ {
using _traits = make_traits<no_value_t, tag::is_union, tag::is_return_value>; using _traits = make_traits<no_value_t, tag::is_union>;
using _nodes = detail::type_vector<Lhs, Rhs>; using _nodes = detail::type_vector<Lhs, Rhs>;
using _data_t = union_data_t<Flag, Lhs, Rhs>; using _data_t = union_data_t<Flag, Lhs, Rhs>;
@ -91,8 +91,6 @@ namespace sqlpp
typename Rhs::_consistency_check>; typename Rhs::_consistency_check>;
}; };
template <typename Statement>
using _result_methods_t = typename Lhs::template _result_methods_t<Statement>;
}; };
SQLPP_PORTABLE_STATIC_ASSERT(assert_union_args_are_statements_t, "arguments for union() must be statements"); SQLPP_PORTABLE_STATIC_ASSERT(assert_union_args_are_statements_t, "arguments for union() must be statements");

View File

@ -43,7 +43,7 @@ namespace sqlpp
struct update_t : public statement_name_t<update_name_t> struct update_t : public statement_name_t<update_name_t>
{ {
using _traits = make_traits<no_value_t, tag::is_return_value>; using _traits = make_traits<no_value_t>;
struct _sqlpp_name_tag struct _sqlpp_name_tag
{ {
}; };
@ -86,6 +86,9 @@ namespace sqlpp
}; };
}; };
template<>
struct is_result_clause<update_t> : public std::true_type {};
template <typename Context> template <typename Context>
auto to_sql_string(Context& , const update_name_t&) -> std::string auto to_sql_string(Context& , const update_name_t&) -> std::string
{ {

View File

@ -33,22 +33,23 @@ namespace sqlpp
namespace detail namespace detail
{ {
template <template <typename> class Predicate, typename Default, typename... T> template <template <typename> class Predicate, typename Default, typename... T>
struct get_last_if_impl; struct get_last_if;
template <template <typename> class Predicate, typename Default> template <template <typename> class Predicate, typename Default>
struct get_last_if_impl<Predicate, Default> struct get_last_if<Predicate, Default>
{ {
using type = Default; using type = Default;
}; };
template <template <typename> class Predicate, typename Default, typename T, typename... Rest> template <template <typename> class Predicate, typename Default, typename T, typename... Rest>
struct get_last_if_impl<Predicate, Default, T, Rest...> struct get_last_if<Predicate, Default, T, Rest...>
{ {
using rest = typename get_last_if_impl<Predicate, Default, Rest...>::type; using type = typename get_last_if<Predicate,
using type = typename std::conditional<std::is_same<rest, Default>::value and Predicate<T>::value, T, rest>::type; typename std::conditional<Predicate<T>::value, T, Default>::type,
Rest...>::type;
}; };
template <template <typename> class Predicate, typename Default, typename... T> template <template <typename> class Predicate, typename Default, typename... T>
using get_last_if = typename get_last_if_impl<Predicate, Default, T...>::type; using get_last_if_t = typename get_last_if<Predicate, Default, T...>::type;
} // namespace detail } // namespace detail
} // namespace sqlpp } // namespace sqlpp

View File

@ -48,6 +48,11 @@ namespace sqlpp
Expr _expr; Expr _expr;
}; };
template <typename KnownAggregateColumns, typename Expr>
struct is_aggregate_expression<KnownAggregateColumns, group_by_column<Expr>> : public std::true_type
{
};
template <typename Expr> template <typename Expr>
struct is_group_by_column<group_by_column<Expr>> : public std::true_type struct is_group_by_column<group_by_column<Expr>> : public std::true_type
{ {

View File

@ -53,6 +53,21 @@ namespace sqlpp
// No value_type_of or name_tag_of defined for as_expression, to prevent its usage outside of select columns. // No value_type_of or name_tag_of defined for as_expression, to prevent its usage outside of select columns.
template <typename T>
struct remove_as
{
using type = T;
};
template <typename Expression, typename AliasProvider>
struct remove_as<as_expression<Expression, AliasProvider>>
{
using type = Expression;
};
template <typename T>
using remove_as_t = typename remove_as<T>::type;
template <typename Expression, typename AliasProvider> template <typename Expression, typename AliasProvider>
struct nodes_of<as_expression<Expression, AliasProvider>> struct nodes_of<as_expression<Expression, AliasProvider>>
{ {

View File

@ -54,7 +54,8 @@ namespace sqlpp
struct custom_parts_t struct custom_parts_t
{ {
using _custom_query_t = custom_query_t<Parts...>; using _custom_query_t = custom_query_t<Parts...>;
using _maybe_hidden_result_type_provider = detail::get_first_if<is_return_value_t, noop, Parts...>; #warning: This should be get_last_if, I think.
using _maybe_hidden_result_type_provider = detail::get_first_if<is_result_clause, noop, Parts...>;
using _result_type_provider = typename unhide<_maybe_hidden_result_type_provider>::type; using _result_type_provider = typename unhide<_maybe_hidden_result_type_provider>::type;
using _result_methods_t = typename _result_type_provider::template _result_methods_t<_result_type_provider>; using _result_methods_t = typename _result_type_provider::template _result_methods_t<_result_type_provider>;
}; };

View File

@ -114,7 +114,7 @@ namespace sqlpp
// The common table expressions not covered by the with. // The common table expressions not covered by the with.
using _required_ctes = detail::make_difference_set_t<_all_required_ctes, _all_provided_ctes>; using _required_ctes = detail::make_difference_set_t<_all_required_ctes, _all_provided_ctes>;
using _result_type_provider = detail::get_last_if<is_return_value_t, noop, Policies...>; using _result_type_provider = detail::get_last_if_t<is_result_clause, noop, Policies...>;
struct _result_methods_t : public _result_type_provider::template _result_methods_t<_statement_t> struct _result_methods_t : public _result_type_provider::template _result_methods_t<_statement_t>
{ {
@ -203,8 +203,10 @@ namespace sqlpp
tag::is_statement, tag::is_statement,
tag_if<tag::is_select, logic::any_t<is_select_t<Policies>::value...>::value>, tag_if<tag::is_select, logic::any_t<is_select_t<Policies>::value...>::value>,
tag_if<tag::is_expression, is_expression_t<_policies_t>::value>, tag_if<tag::is_expression, is_expression_t<_policies_t>::value>,
tag_if<tag::is_selectable, is_expression_t<_policies_t>::value>, tag_if<tag::is_selectable, is_expression_t<_policies_t>::value>
tag_if<tag::is_return_value, logic::none_t<is_noop_t<_result_type_provider>::value>::value>>; #warning: reactivate
//,tag_if<tag::is_return_value, logic::none_t<is_noop_t<_result_type_provider>::value>::value>
>;
using _name_tag_of = name_tag_of<_result_type_provider>; using _name_tag_of = name_tag_of<_result_type_provider>;
using _nodes = detail::type_vector<_policies_t>; using _nodes = detail::type_vector<_policies_t>;
using _used_outer_tables = typename _policies_t::_all_provided_outer_tables; using _used_outer_tables = typename _policies_t::_all_provided_outer_tables;

View File

@ -222,7 +222,6 @@ namespace sqlpp
SQLPP_VALUE_TRAIT_GENERATOR(is_cte) SQLPP_VALUE_TRAIT_GENERATOR(is_cte)
SQLPP_VALUE_TRAIT_GENERATOR(is_noop) SQLPP_VALUE_TRAIT_GENERATOR(is_noop)
SQLPP_VALUE_TRAIT_GENERATOR(is_missing) SQLPP_VALUE_TRAIT_GENERATOR(is_missing)
SQLPP_VALUE_TRAIT_GENERATOR(is_return_value)
SQLPP_VALUE_TRAIT_GENERATOR(is_raw_table) SQLPP_VALUE_TRAIT_GENERATOR(is_raw_table)
SQLPP_VALUE_TRAIT_GENERATOR(is_pre_join) SQLPP_VALUE_TRAIT_GENERATOR(is_pre_join)
SQLPP_VALUE_TRAIT_GENERATOR(is_join) SQLPP_VALUE_TRAIT_GENERATOR(is_join)
@ -590,4 +589,7 @@ namespace sqlpp
template <typename Db> template <typename Db>
using serializer_context_of = typename serializer_context_of_impl<Db>::type; using serializer_context_of = typename serializer_context_of_impl<Db>::type;
template<typename T>
struct is_result_clause : public std::false_type {};
} // namespace sqlpp } // namespace sqlpp

View File

@ -80,5 +80,11 @@ namespace sqlpp
{ {
}; };
// If a GROUP BY clause defines known aggregate columns or the SELECT columns contain an aggregate function then ALL
// columns need to be aggregate.
// SELECT-like clauses will need to specialize this.
template <typename KnownAggregateColumns, typename T>
struct has_correct_aggregates : public std::true_type {};
} // namespace sqlpp11 } // namespace sqlpp11

View File

@ -49,7 +49,7 @@ namespace sqlpp
template <typename InsertOrAlternative> template <typename InsertOrAlternative>
struct insert_or_t : public statement_name_t<InsertOrAlternative> struct insert_or_t : public statement_name_t<InsertOrAlternative>
{ {
using _traits = make_traits<no_value_t, tag::is_return_value>; using _traits = make_traits<no_value_t>;
struct _sqlpp_name_tag struct _sqlpp_name_tag
{ {
}; };
@ -78,6 +78,9 @@ namespace sqlpp
}; };
}; };
template<>
struct is_result_clause<insert_or_t> : public std::true_type {};
template <typename InsertOrAlternative> template <typename InsertOrAlternative>
using blank_insert_or_t = using blank_insert_or_t =
statement_t<insert_or_t<InsertOrAlternative>, no_into_t, no_insert_value_list_t>; statement_t<insert_or_t<InsertOrAlternative>, no_into_t, no_insert_value_list_t>;

View File

@ -38,4 +38,5 @@ test_compile(value)
add_subdirectory(aggregate_function) add_subdirectory(aggregate_function)
add_subdirectory(operator) add_subdirectory(operator)
add_subdirectory(clause) add_subdirectory(clause)
add_subdirectory(detail)
add_subdirectory(type_traits) add_subdirectory(type_traits)

View File

@ -46,12 +46,21 @@ void test_select_columns()
{ {
auto v = sqlpp::value("text"); auto v = sqlpp::value("text");
auto col_int = test::TabFoo{}.id; auto col_int = test::TabFoo{}.id;
auto col_txt = test::TabFoo{}.textNnD;
using unknown = sqlpp::detail::type_vector<>;
using knownInt = sqlpp::detail::type_vector<decltype(col_int)>;
using knownTxt = sqlpp::detail::type_vector<decltype(col_txt)>;
// Single column // Single column
{ {
using T = clause_of_t<decltype(select_columns(col_int))>; using T = clause_of_t<decltype(select_columns(col_int))>;
static_assert(std::is_same<sqlpp::name_tag_of_t<T>, test::TabFoo_::Id::_sqlpp_name_tag>::value, ""); static_assert(std::is_same<sqlpp::name_tag_of_t<T>, test::TabFoo_::Id::_sqlpp_name_tag>::value, "");
static_assert(std::is_same<sqlpp::value_type_of_t<T>, sqlpp::integral>::value, ""); static_assert(std::is_same<sqlpp::value_type_of_t<T>, sqlpp::integral>::value, "");
static_assert(sqlpp::is_result_clause<T>::value, "");
static_assert(sqlpp::has_correct_aggregates<unknown, T>::value, "");
static_assert(sqlpp::has_correct_aggregates<knownInt, T>::value, "");
static_assert(not sqlpp::has_correct_aggregates<knownTxt, T>::value, "");
} }
// Single dynamic column // Single dynamic column
@ -59,6 +68,10 @@ void test_select_columns()
using T = clause_of_t<decltype(select_columns(dynamic(true, col_int)))>; using T = clause_of_t<decltype(select_columns(dynamic(true, col_int)))>;
static_assert(std::is_same<sqlpp::name_tag_of_t<T>, test::TabFoo_::Id::_sqlpp_name_tag>::value, ""); static_assert(std::is_same<sqlpp::name_tag_of_t<T>, test::TabFoo_::Id::_sqlpp_name_tag>::value, "");
static_assert(std::is_same<sqlpp::value_type_of_t<T>, sqlpp::optional<sqlpp::integral>>::value, ""); static_assert(std::is_same<sqlpp::value_type_of_t<T>, sqlpp::optional<sqlpp::integral>>::value, "");
static_assert(sqlpp::is_result_clause<T>::value, "");
static_assert(sqlpp::has_correct_aggregates<unknown, T>::value, "");
static_assert(sqlpp::has_correct_aggregates<knownInt, T>::value, "");
static_assert(not sqlpp::has_correct_aggregates<knownTxt, T>::value, "");
} }
// Single declared group by column // Single declared group by column
@ -66,6 +79,10 @@ void test_select_columns()
using T = clause_of_t<decltype(select_columns(declare_group_by_column(v).as(cheese)))>; using T = clause_of_t<decltype(select_columns(declare_group_by_column(v).as(cheese)))>;
static_assert(std::is_same<sqlpp::name_tag_of_t<T>, cheese_t::_sqlpp_name_tag>::value, ""); static_assert(std::is_same<sqlpp::name_tag_of_t<T>, cheese_t::_sqlpp_name_tag>::value, "");
static_assert(std::is_same<sqlpp::value_type_of_t<T>, sqlpp::text>::value, ""); static_assert(std::is_same<sqlpp::value_type_of_t<T>, sqlpp::text>::value, "");
static_assert(sqlpp::is_result_clause<T>::value, "");
static_assert(sqlpp::has_correct_aggregates<unknown, T>::value, "");
static_assert(sqlpp::has_correct_aggregates<knownInt, T>::value, "");
static_assert(sqlpp::has_correct_aggregates<knownTxt, T>::value, "");
} }
// Single dynamic declared group by column // Single dynamic declared group by column
@ -73,6 +90,10 @@ void test_select_columns()
using T = clause_of_t<decltype(select_columns(dynamic(true, declare_group_by_column(v)).as(cheese)))>; using T = clause_of_t<decltype(select_columns(dynamic(true, declare_group_by_column(v)).as(cheese)))>;
static_assert(std::is_same<sqlpp::name_tag_of_t<T>, cheese_t::_sqlpp_name_tag>::value, ""); static_assert(std::is_same<sqlpp::name_tag_of_t<T>, cheese_t::_sqlpp_name_tag>::value, "");
static_assert(std::is_same<sqlpp::value_type_of_t<T>, sqlpp::optional<sqlpp::text>>::value, ""); static_assert(std::is_same<sqlpp::value_type_of_t<T>, sqlpp::optional<sqlpp::text>>::value, "");
static_assert(sqlpp::is_result_clause<T>::value, "");
static_assert(sqlpp::has_correct_aggregates<unknown, T>::value, "");
static_assert(sqlpp::has_correct_aggregates<knownInt, T>::value, "");
static_assert(sqlpp::has_correct_aggregates<knownTxt, T>::value, "");
} }
#warning: add actual tests here #warning: add actual tests here

View File

@ -0,0 +1,32 @@
# 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.
function(test_compile name)
set(target sqlpp11_core_types_clause_${name})
add_executable(${target} ${name}.cpp)
target_link_libraries(${target} PRIVATE sqlpp11::sqlpp11 sqlpp11_testing)
endfunction()
test_compile(get_last_if)

View File

@ -0,0 +1,54 @@
/*
* 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 "Sample.h"
#include <sqlpp11/sqlpp11.h>
void test_get_last_if()
{
auto v = sqlpp::value("text");
auto col_int = test::TabFoo{}.id;
// Ending on a matching type
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop>, sqlpp::noop>::value, "");
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop, int>, int>::value, "");
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop, int, float, long>, long>::value, "");
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop, int, float, long, short>, short>::value, "");
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop, int, float, long, short, size_t>, short>::value, "");
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_numeric, sqlpp::noop, int, float, long, short, size_t>, size_t>::value, "");
// Ending on a non-matching type
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop, double>, sqlpp::noop>::value, "");
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop, int, double>, int>::value, "");
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop, int, float, double>, int>::value, "");
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop, int, float, long, double>, long>::value, "");
static_assert(std::is_same<sqlpp::detail::get_last_if_t<sqlpp::is_integral, sqlpp::noop, int, float, long, short, double>, short>::value, "");
}
int main()
{
void test_get_last_if();
}