From 3cab459077a1419a106f57810827ec97864c0c76 Mon Sep 17 00:00:00 2001 From: rbock Date: Wed, 17 Sep 2014 22:22:45 +0200 Subject: [PATCH] Added tag to indicate that an expression contains an aggregate function --- examples/select.cpp | 11 ++- include/sqlpp11/avg.h | 4 +- include/sqlpp11/column.h | 4 +- include/sqlpp11/count.h | 14 ++- include/sqlpp11/extra_tables.h | 2 +- include/sqlpp11/join.h | 4 +- include/sqlpp11/max.h | 3 +- include/sqlpp11/min.h | 3 +- include/sqlpp11/on.h | 1 - include/sqlpp11/parameter.h | 2 +- include/sqlpp11/result_field_methods.h | 4 +- include/sqlpp11/statement.h | 16 ++-- include/sqlpp11/sum.h | 4 +- include/sqlpp11/table.h | 2 +- include/sqlpp11/table_alias.h | 2 +- include/sqlpp11/type_traits.h | 125 +++++++++++-------------- include/sqlpp11/verbatim.h | 2 +- test_constraints/CMakeLists.txt | 1 + test_constraints/count_of_count.cpp | 38 ++++++++ 19 files changed, 147 insertions(+), 95 deletions(-) create mode 100644 test_constraints/count_of_count.cpp diff --git a/examples/select.cpp b/examples/select.cpp index b56ba61b..3bcba136 100644 --- a/examples/select.cpp +++ b/examples/select.cpp @@ -34,6 +34,7 @@ #include "MockDb.h" #include +SQLPP_ALIAS_PROVIDER(cheesecake); MockDb db; @@ -42,7 +43,7 @@ test::TabFeature f; int main() { - for (const auto& row : db(select(all_of(p)).from(p).where(true))) + for (const auto& row : db(select(all_of(p)).from(p).where(p.id > 7))) { int64_t id = row.id; std::string name = row.name; @@ -51,7 +52,7 @@ int main() #if 0 - for (const auto& row : db(select(p.name).from(p).where(true))) + for (const auto& row : db(select(p.name).from(p).where(p.name.like("Herb%")))) { int64_t id = row.id; std::string name = row.name; @@ -62,12 +63,12 @@ int main() #if 0 - for (const auto& row : db(select(p.name, f.name).from(p,f).where(true))) + for (const auto& row : db(select(p.name, f.name.as(cheesecake)).from(p,f).where(p.id > 7 and p.feature == 3))) { //int64_t id = row.id; //std::string a = row.a; std::string name = row.name; - //int64_t feature = row.feature; + std::string feature = row.cheesecake; } #endif @@ -90,7 +91,7 @@ int main() -#if 0 +#if !0 auto s = select(all_of(p)) .from(p, f) .where(p.name == any(select(f.name) diff --git a/include/sqlpp11/avg.h b/include/sqlpp11/avg.h index 4b879ef8..52ae5652 100644 --- a/include/sqlpp11/avg.h +++ b/include/sqlpp11/avg.h @@ -37,7 +37,7 @@ namespace sqlpp public alias_operators> { using _traits = make_traits; - using _recursive_traits = make_recursive_traits; + using _recursive_traits = make_recursive_traits; static_assert(is_noop::value or std::is_same::value, "avg() used with flag other than 'distinct'"); static_assert(is_numeric_t::value, "avg() requires a value expression as argument"); @@ -89,6 +89,7 @@ namespace sqlpp template auto avg(T t) -> avg_t> { + static_assert(not contains_aggregate_function_t>::value, "avg() cannot be used on an aggregate function"); static_assert(is_numeric_t>::value, "avg() requires a value expression as argument"); return { t }; } @@ -96,6 +97,7 @@ namespace sqlpp template auto avg(const distinct_t&, T t) -> avg_t> { + static_assert(not contains_aggregate_function_t>::value, "avg() cannot be used on an aggregate function"); static_assert(is_numeric_t>::value, "avg() requires a value expression as argument"); return { t }; } diff --git a/include/sqlpp11/column.h b/include/sqlpp11/column.h index 31c9382b..22b5d977 100644 --- a/include/sqlpp11/column.h +++ b/include/sqlpp11/column.h @@ -59,7 +59,9 @@ namespace sqlpp using _provided_outer_tables = detail::type_set<>; using _required_tables = detail::type_set; using _extra_tables = detail::type_set<>; - using _can_be_null = column_spec_can_be_null_t; + using _tags = typename std::conditional::value, + detail::type_set, + detail::type_set<>>::type; }; using _spec_t = ColumnSpec; diff --git a/include/sqlpp11/count.h b/include/sqlpp11/count.h index 2ed9766d..3dee29ac 100644 --- a/include/sqlpp11/count.h +++ b/include/sqlpp11/count.h @@ -38,7 +38,17 @@ namespace sqlpp public alias_operators> { using _traits = make_traits; - using _recursive_traits = make_recursive_traits; + struct _recursive_traits + { + using _required_tables = required_tables_of; + using _provided_tables = provided_tables_of; + using _provided_outer_tables = provided_outer_tables_of; + using _extra_tables = extra_tables_of; + using _parameters = parameters_of; + using _tags = detail::make_difference_set_t, recursive_tags_of>, + detail::type_set>; + }; + static_assert(is_noop::value or std::is_same::value, "count() used with flag other than 'distinct'"); static_assert(is_expression_t::value, "count() requires a sql expression as argument"); @@ -90,6 +100,7 @@ namespace sqlpp template auto count(T t) -> count_t> { + static_assert(not contains_aggregate_function_t>::value, "count() cannot be used on an aggregate function"); static_assert(is_expression_t>::value, "count() requires an expression as argument"); return { t }; } @@ -97,6 +108,7 @@ namespace sqlpp template auto count(const distinct_t&, T t) -> count_t> { + static_assert(not contains_aggregate_function_t>::value, "count() cannot be used on an aggregate function"); static_assert(is_expression_t>::value, "count() requires an expression as argument"); return { t }; } diff --git a/include/sqlpp11/extra_tables.h b/include/sqlpp11/extra_tables.h index 22623b0d..d2fe8a73 100644 --- a/include/sqlpp11/extra_tables.h +++ b/include/sqlpp11/extra_tables.h @@ -59,7 +59,7 @@ namespace sqlpp using _provided_outer_tables = detail::type_set<>; using _provided_tables = detail::type_set<>; using _extra_tables = detail::type_set; - using _can_be_null = std::false_type; + using _tags = detail::type_set<>; }; // FIXME: extra_tables must not require tables! diff --git a/include/sqlpp11/join.h b/include/sqlpp11/join.h index 30a3db7b..e7b444a7 100644 --- a/include/sqlpp11/join.h +++ b/include/sqlpp11/join.h @@ -73,7 +73,7 @@ namespace sqlpp using _provided_outer_tables = typename JoinType::template _provided_outer_tables; using _extra_tables = detail::make_joined_set_t, extra_tables_of>; using _parameters = detail::make_parameter_tuple_t, parameters_of>; - using _can_be_null = std::false_type; + using _tags = detail::type_set<>; }; @@ -94,6 +94,8 @@ namespace sqlpp -> set_on_t> { static_assert(is_noop::value, "cannot call on() twice for a single join()"); + static_assert(detail::all_t::value...>::value, "at least one argument is not an expression in on()"); + return { _lhs, _rhs, {std::tuple{expr...}} diff --git a/include/sqlpp11/max.h b/include/sqlpp11/max.h index 8f0f8fb3..95b182a5 100644 --- a/include/sqlpp11/max.h +++ b/include/sqlpp11/max.h @@ -37,7 +37,7 @@ namespace sqlpp public alias_operators> { using _traits = make_traits, tag::is_expression, tag::is_named_expression>; - using _recursive_traits = make_recursive_traits; + using _recursive_traits = make_recursive_traits; static_assert(is_expression_t::value, "max() requires a value expression as argument"); @@ -83,6 +83,7 @@ namespace sqlpp template auto max(T t) -> max_t> { + static_assert(not contains_aggregate_function_t>::value, "max() cannot be used on an aggregate function"); static_assert(is_expression_t>::value, "max() requires a value expression as argument"); return { t }; } diff --git a/include/sqlpp11/min.h b/include/sqlpp11/min.h index 7846d763..27c923ad 100644 --- a/include/sqlpp11/min.h +++ b/include/sqlpp11/min.h @@ -37,7 +37,7 @@ namespace sqlpp public alias_operators> { using _traits = make_traits, tag::is_expression, tag::is_named_expression>; - using _recursive_traits = make_recursive_traits; + using _recursive_traits = make_recursive_traits; static_assert(is_expression_t::value, "min() requires a value expression as argument"); @@ -83,6 +83,7 @@ namespace sqlpp template auto min(T t) -> min_t> { + static_assert(not contains_aggregate_function_t>::value, "min() cannot be used on an aggregate function"); static_assert(is_expression_t>::value, "min() requires a value expression as argument"); return { t }; } diff --git a/include/sqlpp11/on.h b/include/sqlpp11/on.h index 945d90ec..2a88eafb 100644 --- a/include/sqlpp11/on.h +++ b/include/sqlpp11/on.h @@ -43,7 +43,6 @@ namespace sqlpp using _is_dynamic = is_database; static_assert(_is_dynamic::value or sizeof...(Expr), "at least one expression argument required in on()"); - static_assert(detail::all_t::value...>::value, "at least one argument is not an expression in on()"); template void add(E expr) diff --git a/include/sqlpp11/parameter.h b/include/sqlpp11/parameter.h index a789f56f..38f100c1 100644 --- a/include/sqlpp11/parameter.h +++ b/include/sqlpp11/parameter.h @@ -45,7 +45,7 @@ namespace sqlpp using _provided_outer_tables = detail::type_set<>; using _required_tables = detail::type_set<>; using _extra_tables = detail::type_set<>; - using _can_be_null = std::true_type; + using _tags = detail::type_set; }; using _instance_t = member_t>; diff --git a/include/sqlpp11/result_field_methods.h b/include/sqlpp11/result_field_methods.h index 54492a38..5829ba91 100644 --- a/include/sqlpp11/result_field_methods.h +++ b/include/sqlpp11/result_field_methods.h @@ -88,7 +88,9 @@ namespace sqlpp using _provided_outer_tables = detail::type_set<>; using _required_tables = detail::type_set<>; using _extra_tables = detail::type_set<>; - using _can_be_null = column_spec_can_be_null_t<_field_spec_t>; + using _tags = typename std::conditional::value, + detail::type_set, + detail::type_set<>>::type; }; }; diff --git a/include/sqlpp11/statement.h b/include/sqlpp11/statement.h index e7a3778e..81c376ea 100644 --- a/include/sqlpp11/statement.h +++ b/include/sqlpp11/statement.h @@ -98,6 +98,13 @@ namespace sqlpp no_value_t // if a required statement part is missing (e.g. columns in a select), then the statement cannot be used as a value >::type; + using _can_be_null = detail::any_t< + can_be_null_t<_result_type_provider>::value, + detail::make_intersect_set_t< + required_tables_of<_result_type_provider>, + _all_provided_outer_tables + >::size::value != 0>; + using _traits = make_traits<_value_type, tag_if::value>>; struct _recursive_traits @@ -107,12 +114,9 @@ namespace sqlpp using _provided_outer_tables = detail::type_set<>; using _extra_tables = detail::type_set<>; using _parameters = detail::make_parameter_tuple_t...>; - using _can_be_null = detail::any_t< - can_be_null_t<_result_type_provider>::value, - detail::make_intersect_set_t< - required_tables_of<_result_type_provider>, - provided_outer_tables_of - >::size::value != 0>; + using _tags = typename std::conditional<_can_be_null::value, + detail::type_set, + detail::type_set<>>::type; }; }; } diff --git a/include/sqlpp11/sum.h b/include/sqlpp11/sum.h index 468ac230..0a5679a1 100644 --- a/include/sqlpp11/sum.h +++ b/include/sqlpp11/sum.h @@ -37,7 +37,7 @@ namespace sqlpp public alias_operators> { using _traits = make_traits, tag::is_expression, tag::is_named_expression>; - using _recursive_traits = make_recursive_traits; + using _recursive_traits = make_recursive_traits; static_assert(is_noop::value or std::is_same::value, "sum() used with flag other than 'distinct'"); static_assert(is_numeric_t::value, "sum() requires a numeric expression as argument"); @@ -89,6 +89,7 @@ namespace sqlpp template auto sum(T t) -> sum_t> { + static_assert(not contains_aggregate_function_t>::value, "sum() cannot be used on an aggregate function"); static_assert(is_numeric_t>::value, "sum() requires a numeric expression as argument"); return { t }; } @@ -96,6 +97,7 @@ namespace sqlpp template auto sum(const distinct_t&, T t) -> sum_t> { + static_assert(not contains_aggregate_function_t>::value, "sum() cannot be used on an aggregate function"); static_assert(is_numeric_t>::value, "sum() requires a numeric expression as argument"); return { t }; } diff --git a/include/sqlpp11/table.h b/include/sqlpp11/table.h index 3c370279..a90605be 100644 --- a/include/sqlpp11/table.h +++ b/include/sqlpp11/table.h @@ -53,7 +53,7 @@ namespace sqlpp using _provided_tables = detail::type_set
; using _provided_outer_tables = detail::type_set<>; using _extra_tables = detail::type_set<>; - using _can_be_null = std::false_type; + using _tags = detail::type_set<>; }; static_assert(sizeof...(ColumnSpec), "at least one column required per table"); diff --git a/include/sqlpp11/table_alias.h b/include/sqlpp11/table_alias.h index aa95cd83..28425f10 100644 --- a/include/sqlpp11/table_alias.h +++ b/include/sqlpp11/table_alias.h @@ -49,7 +49,7 @@ namespace sqlpp using _provided_tables = detail::type_set; using _provided_outer_tables = detail::type_set<>; using _extra_tables = detail::type_set<>; - using _can_be_null = std::false_type; + using _tags = detail::type_set<>; }; static_assert(required_tables_of
::size::value == 0, "table aliases must not depend on external tables"); diff --git a/include/sqlpp11/type_traits.h b/include/sqlpp11/type_traits.h index 99d12c22..cfc54987 100644 --- a/include/sqlpp11/type_traits.h +++ b/include/sqlpp11/type_traits.h @@ -32,28 +32,40 @@ namespace sqlpp { + namespace tag + { + struct can_be_null{}; + struct contains_aggregate_function{}; + }; + namespace detail { template struct can_be_null_impl { using type = std::false_type; }; template - struct can_be_null_impl::type> { using type = std::true_type; }; + struct can_be_null_impl::value>::type> { using type = std::true_type; }; } template using can_be_null_t = typename detail::can_be_null_impl::type; - namespace tag\ - {\ - struct can_be_null{};\ - };\ - namespace detail\ - {\ - template\ - struct column_spec_can_be_null_impl { using type = std::false_type; };\ - template\ - struct column_spec_can_be_null_impl::value>::type> { using type = std::true_type; };\ - }\ - template\ + namespace detail + { + template + struct contains_aggregate_function_impl { using type = std::false_type; }; + template + struct contains_aggregate_function_impl::value>::type> { using type = std::true_type; }; + } + template + using contains_aggregate_function_t = typename detail::contains_aggregate_function_impl::type; + + namespace detail + { + template + struct column_spec_can_be_null_impl { using type = std::false_type; }; + template + struct column_spec_can_be_null_impl::value>::type> { using type = std::true_type; }; + } + template using column_spec_can_be_null_t = typename detail::column_spec_can_be_null_impl::type; #define SQLPP_VALUE_TRAIT_GENERATOR(name) \ @@ -140,80 +152,35 @@ namespace sqlpp namespace detail { - template - struct value_type_of_impl - { - using type = typename T::_traits::_value_type; - }; - - template - struct required_table_of_impl - { - using type = typename T::_recursive_traits::_required_tables; - }; - - template - struct provided_table_of_impl - { - using type = typename T::_recursive_traits::_provided_tables; - }; - - template - struct provided_outer_table_of_impl - { - using type = typename T::_recursive_traits::_provided_outer_tables; - }; - - template - struct extra_table_of_impl - { - using type = typename T::_recursive_traits::_extra_tables; - }; - - template - struct parameters_of_impl - { - using type = typename T::_recursive_traits::_parameters; - }; - - template - struct name_of_impl - { - using type = typename T::_name_t; - }; - template - struct make_parameter_tuple_impl - { - using type = decltype(std::tuple_cat(std::declval()...)); - }; - - template - using make_parameter_tuple_t = typename make_parameter_tuple_impl::type; + using make_parameter_tuple_t = decltype(std::tuple_cat(std::declval()...)); } template - using value_type_of = typename detail::value_type_of_impl::type; + using value_type_of = typename T::_traits::_value_type; template using cpp_value_type_of = typename value_type_of::_cpp_value_type; template - using required_tables_of = typename detail::required_table_of_impl::type; + using required_tables_of = typename T::_recursive_traits::_required_tables; template - using provided_tables_of = typename detail::provided_table_of_impl::type; + using provided_tables_of = typename T::_recursive_traits::_provided_tables; template - using provided_outer_tables_of = typename detail::provided_outer_table_of_impl::type; + using provided_outer_tables_of = typename T::_recursive_traits::_provided_outer_tables; template - using extra_tables_of = typename detail::extra_table_of_impl::type; + using extra_tables_of = typename T::_recursive_traits::_extra_tables; template - using parameters_of = typename detail::parameters_of_impl::type; + using parameters_of = typename T::_recursive_traits::_parameters; template - using name_of = typename detail::name_of_impl::type; + using recursive_tags_of = typename T::_recursive_traits::_tags; + + template + using name_of = typename T::_name_t; template struct make_traits @@ -221,6 +188,7 @@ namespace sqlpp using _value_type = ValueType; using _tags = detail::make_type_set_t; }; + template struct make_recursive_traits { @@ -229,9 +197,26 @@ namespace sqlpp using _provided_outer_tables = detail::make_joined_set_t...>; using _extra_tables = detail::make_joined_set_t...>; using _parameters = detail::make_parameter_tuple_t...>; - using _can_be_null = detail::any_t::value...>; + using _tags = detail::make_joined_set_t...>; }; + template + struct recursive_tags + { + using _required_tables = detail::type_set<>; + using _provided_tables = detail::type_set<>; + using _provided_outer_tables = detail::type_set<>; + using _extra_tables = detail::type_set<>; + using _parameters = std::tuple<>; + using _tags = detail::type_set; + }; + + struct aggregate_function + { + struct _traits { using _value_type = void; using _tags = detail::type_set<>; }; + using _recursive_traits = recursive_tags; + }; + template using member_t = typename NameProvider::_name_t::template _member_t; diff --git a/include/sqlpp11/verbatim.h b/include/sqlpp11/verbatim.h index 0ea6c61a..d1b43006 100644 --- a/include/sqlpp11/verbatim.h +++ b/include/sqlpp11/verbatim.h @@ -40,7 +40,7 @@ namespace sqlpp using _traits = make_traits; struct _recursive_traits : public make_recursive_traits<> { - using _can_be_null = std::true_type; // since we do not know what's going on inside the verbatim, we assume it can be null + using _tags = detail::type_set; // since we do not know what's going on inside the verbatim, we assume it can be null }; verbatim_t(std::string verbatim): _verbatim(verbatim) {} diff --git a/test_constraints/CMakeLists.txt b/test_constraints/CMakeLists.txt index aec592e4..cfa55c46 100644 --- a/test_constraints/CMakeLists.txt +++ b/test_constraints/CMakeLists.txt @@ -24,6 +24,7 @@ function(test_constraint name pattern) endfunction(test_constraint) +test_constraint(count_of_count "cannot be used on an aggregate function") test_constraint(no_conversion_operator_if_null_not_trivial "int i = row.alpha") test_constraint(require_insert "required column is missing") test_constraint(must_not_insert "one assignment is prohibited") diff --git a/test_constraints/count_of_count.cpp b/test_constraints/count_of_count.cpp new file mode 100644 index 00000000..95f089db --- /dev/null +++ b/test_constraints/count_of_count.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013-2014, 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 "MockDb.h" +#include +#include + +MockDb db; + +int main() +{ + test::TabBar t; + + count(count(t.alpha)); +}