Merge branch 'feature/detect_mixing_aggreage_non_aggregate' into develop

This commit is contained in:
rbock
2015-10-03 20:14:27 +02:00
14 changed files with 184 additions and 5 deletions

View File

@@ -60,6 +60,7 @@ namespace sqlpp
{
using _traits = make_traits<floating_point, tag::is_expression, tag::is_selectable>;
using _nodes = detail::type_vector<Expr, aggregate_function>;
using _is_aggregate_expression = std::true_type;
static_assert(is_noop<Flag>::value or std::is_same<distinct_t, Flag>::value,
"avg() used with flag other than 'distinct'");

View File

@@ -62,6 +62,7 @@ namespace sqlpp
using _traits = make_traits<integral, tag::is_expression /*, tag::is_selectable*/>;
using _nodes = detail::type_vector<Expr, aggregate_function>;
using _is_aggregate_expression = std::true_type;
using _can_be_null = std::false_type;
static_assert(is_noop<Flag>::value or std::is_same<distinct_t, Flag>::value,

View File

@@ -75,6 +75,9 @@ namespace sqlpp
using _nodes = detail::type_vector<Expressions...>;
using _is_dynamic = is_database<Database>;
using _provided_aggregates = typename std::conditional<_is_dynamic::value,
detail::type_set<>,
detail::make_type_set_t<Expressions...>>::type;
// Data
using _data_t = group_by_data_t<Database, Expressions...>;

View File

@@ -39,14 +39,14 @@ namespace sqlpp
// see http://lists.boost.org/Archives/boost/2014/05/212946.php :-)
template <bool... B>
using all_t = std::is_same<logic_helper<B...>, logic_helper<(B, true)...>>;
using all_t = std::is_same<logic_helper<B...>, logic_helper<(B or true)...>>;
template <bool... B>
using any_t =
std::integral_constant<bool, not std::is_same<logic_helper<B...>, logic_helper<(B, false)...>>::value>;
std::integral_constant<bool, not std::is_same<logic_helper<B...>, logic_helper<(B and false)...>>::value>;
template <bool... B>
using none_t = std::is_same<logic_helper<B...>, logic_helper<(B, false)...>>;
using none_t = std::is_same<logic_helper<B...>, logic_helper<(B or false)...>>;
template <bool>
struct not_impl;

View File

@@ -59,6 +59,7 @@ namespace sqlpp
{
using _traits = make_traits<value_type_of<Expr>, tag::is_expression, tag::is_selectable>;
using _nodes = detail::type_vector<Expr, aggregate_function>;
using _is_aggregate_expression = std::true_type;
using _auto_alias_t = max_alias_t;

View File

@@ -59,6 +59,7 @@ namespace sqlpp
{
using _traits = make_traits<value_type_of<Expr>, tag::is_expression, tag::is_selectable>;
using _nodes = detail::type_vector<Expr, aggregate_function>;
using _is_aggregate_expression = std::true_type;
using _auto_alias_t = min_alias_t;

View File

@@ -168,6 +168,17 @@ namespace sqlpp
}
};
struct assert_aggregates_t
{
using type = std::false_type;
template <typename T = void>
static void _()
{
static_assert(wrong_t<T>::value, "not all columns are made of aggregates, despite group_by or similar");
}
};
// SELECTED COLUMNS
template <typename Database, typename... Columns>
struct select_column_list_t
@@ -262,10 +273,16 @@ namespace sqlpp
return t.selected_columns;
}
using _consistency_check =
using _column_check =
typename std::conditional<Policies::template _no_unknown_tables<select_column_list_t>::value,
consistent_t,
assert_no_unknown_tables_in_selected_columns_t>::type;
using _aggregate_check = typename std::conditional<Policies::template _no_unknown_aggregates<Columns...>::value,
consistent_t,
assert_aggregates_t>::type;
using _consistency_check = detail::get_first_if<is_inconsistent_t, consistent_t, _column_check, _aggregate_check>;
};
// Result methods

View File

@@ -103,12 +103,18 @@ namespace sqlpp
using _all_provided_tables = detail::make_joined_set_t<provided_tables_of<Policies>...>;
using _all_provided_outer_tables = detail::make_joined_set_t<provided_outer_tables_of<Policies>...>;
using _all_extra_tables = detail::make_joined_set_t<extra_tables_of<Policies>...>;
using _all_provided_aggregates = detail::make_joined_set_t<provided_aggregates_of<Policies>...>;
using _known_tables = detail::make_joined_set_t<_all_provided_tables, _all_extra_tables>;
template <typename Expression>
using _no_unknown_tables = detail::is_subset_of<required_tables_of<Expression>, _known_tables>;
template <typename... Expressions>
using _no_unknown_aggregates =
logic::any_t<_all_provided_aggregates::size::value == 0,
logic::all_t<is_aggregate_expression_t<_all_provided_aggregates, Expressions>::value...>::value>;
template <template <typename> class Predicate>
using any_t = logic::any_t<Predicate<Policies>::value...>;

View File

@@ -60,6 +60,7 @@ namespace sqlpp
{
using _traits = make_traits<value_type_of<Expr>, tag::is_expression, tag::is_selectable>;
using _nodes = detail::type_vector<Expr, aggregate_function>;
using _is_aggregate_expression = std::true_type;
static_assert(is_noop<Flag>::value or std::is_same<distinct_t, Flag>::value,
"sum() used with flag other than 'distinct'");

View File

@@ -195,6 +195,7 @@ namespace sqlpp
SQLPP_RECURSIVE_TRAIT_SET_GENERATOR(provided_tables)
SQLPP_RECURSIVE_TRAIT_SET_GENERATOR(provided_outer_tables)
SQLPP_RECURSIVE_TRAIT_SET_GENERATOR(extra_tables)
SQLPP_RECURSIVE_TRAIT_SET_GENERATOR(provided_aggregates)
#define SQLPP_RECURSIVE_TRAIT_GENERATOR(trait) \
namespace detail \
@@ -221,6 +222,38 @@ namespace sqlpp
SQLPP_RECURSIVE_TRAIT_GENERATOR(can_be_null)
SQLPP_RECURSIVE_TRAIT_GENERATOR(contains_aggregate_function)
namespace detail
{
template <typename KnownAggregates, typename T, typename Leaf = void>
struct is_aggregate_expression_impl
{
using type = typename is_aggregate_expression_impl<KnownAggregates, typename T::_nodes>::type;
};
template <typename KnownAggregates, typename T>
struct is_aggregate_expression_impl<
KnownAggregates,
T,
typename std::enable_if<std::is_class<typename T::_is_aggregate_expression>::value>::type>
{
using type = typename T::_is_aggregate_expression;
};
template <typename KnownAggregates, typename T>
struct is_aggregate_expression_impl<KnownAggregates,
T,
typename std::enable_if<detail::is_element_of<T, KnownAggregates>::value>::type>
{
using type = std::true_type;
};
template <typename KnownAggregates, typename... Nodes>
struct is_aggregate_expression_impl<KnownAggregates, type_vector<Nodes...>, void>
{
using type =
logic::all_t<sizeof...(Nodes) != 0, is_aggregate_expression_impl<KnownAggregates, Nodes>::type::value...>;
};
}
template <typename KnownAggregates, typename T>
using is_aggregate_expression_t = typename detail::is_aggregate_expression_impl<KnownAggregates, T>::type;
namespace detail
{
template <typename T, typename Leaf = void>

View File

@@ -44,6 +44,7 @@ namespace sqlpp
{
using _traits = make_traits<boolean, tag::is_expression, tag::is_wrapped_value>;
using _nodes = detail::type_vector<>;
using _is_aggregate_expression = std::true_type;
using _value_t = bool;
@@ -86,6 +87,7 @@ namespace sqlpp
{
using _traits = make_traits<integral, tag::is_expression, tag::is_wrapped_value>;
using _nodes = detail::type_vector<>;
using _is_aggregate_expression = std::true_type;
using _value_t = int64_t;
@@ -128,6 +130,7 @@ namespace sqlpp
{
using _traits = make_traits<floating_point, tag::is_expression, tag::is_wrapped_value>;
using _nodes = detail::type_vector<>;
using _is_aggregate_expression = std::true_type;
using _value_t = double;
@@ -170,6 +173,7 @@ namespace sqlpp
{
using _traits = make_traits<text, tag::is_expression, tag::is_wrapped_value>;
using _nodes = detail::type_vector<>;
using _is_aggregate_expression = std::true_type;
using _value_t = std::string;

110
tests/Aggregates.cpp Normal file
View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2013-2015, 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 <iostream>
#include "Sample.h"
#include "MockDb.h"
#include <sqlpp11/alias_provider.h>
#include <sqlpp11/select.h>
#include <sqlpp11/functions.h>
#include <sqlpp11/connection.h>
namespace sqlpp
{
namespace test
{
template <typename T>
void print_type_on_error(std::true_type, const T&)
{
}
template <typename T>
void print_type_on_error(std::false_type, const T& t)
{
t._print_me_;
}
template <typename Assert, typename Expression>
void run_check(const Expression&)
{
using Context = MockDb::_serializer_context_t;
using CheckResult = std::is_same<sqlpp::run_check_t<Context, Expression>, Assert>;
static_assert(CheckResult::value, "Unexpected run_check result");
print_type_on_error(CheckResult{}, sqlpp::run_check_t<Context, Expression>{});
}
template <typename Expression>
void run_check(const Expression&)
{
using Context = MockDb::_serializer_context_t;
using CheckResult = std::is_same<sqlpp::run_check_t<Context, Expression>, consistent_t>;
static_assert(CheckResult::value, "Unexpected run_check result");
print_type_on_error(CheckResult{}, sqlpp::run_check_t<Context, Expression>{});
}
}
}
SQLPP_ALIAS_PROVIDER(whatever);
int Aggregates(int, char**)
{
using sqlpp::test::run_check;
// test::TabFoo f;
test::TabBar t;
// If there is no group_by, we can select whatever we want
run_check(select(all_of(t)).from(t).where(true));
run_check(select(t.alpha).from(t).where(true));
run_check(select(count(t.alpha)).from(t).where(true));
// If there is a static group_by, selected columns must be made of group_by expressions, or aggregate expression (e.g.
// count(t.id)) or values to be valid
run_check(select(t.alpha).from(t).where(true).group_by(t.alpha));
run_check(select((t.alpha + 42).as(whatever)).from(t).where(true).group_by(t.alpha));
run_check(select((t.alpha + 42).as(whatever)).from(t).where(true).group_by(t.alpha, t.alpha + t.delta * 17));
run_check(
select((t.alpha + t.delta * 17).as(whatever)).from(t).where(true).group_by(t.alpha, t.alpha + t.delta * 17));
run_check(select((t.beta + "fortytwo").as(whatever)).from(t).where(true).group_by(t.beta));
run_check(select(avg(t.alpha)).from(t).where(true).group_by(t.beta));
run_check(select(count(t.alpha)).from(t).where(true).group_by(t.beta));
run_check(select(max(t.alpha)).from(t).where(true).group_by(t.beta));
run_check(select(min(t.alpha)).from(t).where(true).group_by(t.beta));
run_check(select(sum(t.alpha)).from(t).where(true).group_by(t.beta));
run_check(select((t.alpha + count(t.delta)).as(whatever)).from(t).where(true).group_by(t.alpha));
run_check(select(sqlpp::value(1).as(whatever)).from(t).where(true).group_by(t.alpha));
run_check(select(sqlpp::value("whatever").as(whatever)).from(t).where(true).group_by(t.alpha));
// Otherwise, they are invalid
run_check<sqlpp::assert_aggregates_t>(select(t.beta).from(t).where(true).group_by(t.alpha));
run_check<sqlpp::assert_aggregates_t>(select((t.alpha + t.delta).as(whatever)).from(t).where(true).group_by(t.alpha));
run_check<sqlpp::assert_aggregates_t>(
select((t.alpha + t.delta).as(whatever)).from(t).where(true).group_by(t.alpha, t.alpha + t.delta * 17));
return 0;
}

View File

@@ -30,6 +30,7 @@ target_compile_options(sqlpp11_testing INTERFACE -Wall -Wextra -pedantic)
endif ()
set(test_names
Aggregates
BooleanExpression
CustomQuery
Interpret

View File

@@ -375,7 +375,7 @@ int SelectType(int, char**)
static_assert(sqlpp::is_boolean_t<decltype(select(r.a).from(r))>::value, "select(bool) has to be a bool");
auto s1 = sqlpp::select()
.flags(sqlpp::distinct, sqlpp::straight_join)
.columns(l.alpha, l.beta, select(r.a).from(r))
.columns(l.gamma, r.a)
.from(r, t, l)
.where(t.beta == "hello world" and select(t.gamma).from(t)) // .as(alias::right))
.group_by(l.gamma, r.a)