diff --git a/include/sqlpp11/cross_join.h b/include/sqlpp11/cross_join.h new file mode 100644 index 00000000..77caa374 --- /dev/null +++ b/include/sqlpp11/cross_join.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013-2016, 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. + */ + +#ifndef SQLPP_CROSS_JOIN_H +#define SQLPP_CROSS_JOIN_H + +#include +#include +#include + +namespace sqlpp +{ + template + struct join_t; + + template + struct cross_join_t + { + using _traits = make_traits; + using _nodes = detail::type_vector; + using _can_be_null = std::false_type; + + static_assert(is_table_t::value, "lhs argument for join() has to be a table or join"); + static_assert(is_table_t::value, "rhs argument for join() has to be a table"); + static_assert(not is_join_t::value, "rhs argument for join must not be a join"); + + static_assert(detail::is_disjunct_from, provided_tables_of>::value, + "joined tables must not be identical"); + + static_assert(required_tables_of::size::value == 0, "joined tables must not depend on other tables"); + + template + auto on(Expr expr) -> join_t> + { + static_assert(is_expression_t::value, "argument is not an expression in on()"); + + static_assert(is_boolean_t::value, "argument is not a boolean expression in on()"); + + return {*this, {expr, {}}}; + } + + auto unconditionally() -> join_t> + { + return {*this, {}}; + } + + Lhs _lhs; + Rhs _rhs; + }; + + template + struct serializer_t> + { + using _serialize_check = serialize_check_of; + using T = cross_join_t; + + static Context& _(const T& t, Context& context) + { + serialize(t._lhs, context); + context << JoinType::_name; + context << " JOIN "; + serialize(t._rhs, context); + return context; + } + }; +} + +#endif diff --git a/include/sqlpp11/dynamic_join.h b/include/sqlpp11/dynamic_join.h index 1ec70153..20dc0930 100644 --- a/include/sqlpp11/dynamic_join.h +++ b/include/sqlpp11/dynamic_join.h @@ -50,14 +50,14 @@ namespace sqlpp template using set_on_t = dynamic_join_t; - template - auto on(Expr... expr) -> set_on_t> + template + auto on(Expr expr) -> set_on_t> { static_assert(is_noop::value, "cannot call on() twice for a single join()"); - static_assert(logic::all_t::value...>::value, - "at least one argument is not an expression in on()"); + static_assert(is_expression_t::value, "argument is not a boolean expression in on()"); + static_assert(is_boolean_t::value, "argument is not a boolean expression in on()"); - return {_rhs, {std::tuple{expr...}, {}}}; + return {_rhs, {expr, {}}}; } auto unconditionally() -> set_on_t> diff --git a/include/sqlpp11/from.h b/include/sqlpp11/from.h index 8604e955..83b906fe 100644 --- a/include/sqlpp11/from.h +++ b/include/sqlpp11/from.h @@ -147,6 +147,39 @@ namespace sqlpp }; }; + SQLPP_PORTABLE_STATIC_ASSERT( + assert_from_not_cross_join_t, + "from() argument is a cross join, please use an explicit on() condition or unconditionally()"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_from_table_t, "from() argument has to be a table or join expression"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_from_dependency_free_t, "at least one table depends on another table in from()"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_from_no_duplicates_t, "at least one duplicate table name detected in from()"); + + SQLPP_PORTABLE_STATIC_ASSERT(assert_from_dynamic_statement_dynamic_t, + "dynamic_from must not be called in a static statement"); + + template + struct check_from + { + using type = static_combined_check_t< + static_check_t::value, assert_from_not_cross_join_t>, + static_check_t::value, assert_from_table_t>, + static_check_t::size::value == 0, assert_from_dependency_free_t>, + static_check_t::size::value == + detail::make_name_of_set_t>::size::value, + assert_from_no_duplicates_t>>; + }; + + template + using check_from_t = typename check_from::type; + + template + using check_from_static_t = check_from_t
; + + template + using check_from_dynamic_t = static_combined_check_t< + static_check_t::value, assert_from_dynamic_statement_dynamic_t>, + check_from_t
>; + struct no_from_t { using _traits = make_traits; @@ -199,33 +232,26 @@ namespace sqlpp using _database_t = typename Policies::_database_t; - // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 - // template - // using _check = logic::all_t::value...>; - template - struct _check : logic::all_t::value...> - { - }; - template using _new_statement_t = new_statement_t; using _consistency_check = consistent_t; template - auto from(Table table) const -> _new_statement_t<_check
, from_t>> + auto from(Table table) const -> _new_statement_t, from_t>> { - static_assert(_check
::value, "argument is not a table or join in from()"); - return _from_impl(_check
{}, table); + using Check = check_from_static_t
; + Check{}._(); + return _from_impl(Check{}, table); } template - auto dynamic_from(Table table) const -> _new_statement_t<_check
, from_t<_database_t, from_table_t
>> + auto dynamic_from(Table table) const + -> _new_statement_t, from_t<_database_t, from_table_t
>> { - static_assert(not std::is_same<_database_t, void>::value, - "dynamic_from must not be called in a static statement"); - static_assert(_check
::value, "argument is not a table or join in from()"); - return _from_impl<_database_t>(_check
{}, table); + using Check = check_from_dynamic_t<_database_t, Table>; + Check{}._(); + return _from_impl<_database_t>(Check{}, table); } private: @@ -236,17 +262,6 @@ namespace sqlpp auto _from_impl(const std::true_type&, Table table) const -> _new_statement_t>> { - static_assert(required_tables_of>::size::value == 0, - "at least one table depends on another table in from()"); - - static constexpr std::size_t _number_of_tables = detail::sum(provided_tables_of
::size::value); - using _unique_tables = provided_tables_of
; - using _unique_table_names = detail::make_name_of_set_t<_unique_tables>; - static_assert(_number_of_tables == _unique_tables::size::value, - "at least one duplicate table detected in from()"); - static_assert(_number_of_tables == _unique_table_names::size::value, - "at least one duplicate table name detected in from()"); - return {static_cast&>(*this), from_data_t>{from_table(table)}}; } diff --git a/include/sqlpp11/join.h b/include/sqlpp11/join.h index 67e8643f..86c92d1f 100644 --- a/include/sqlpp11/join.h +++ b/include/sqlpp11/join.h @@ -28,99 +28,70 @@ #define SQLPP_JOIN_H #include +#include #include -#include namespace sqlpp { - template + template struct join_t { using _traits = make_traits; - using _nodes = detail::type_vector; + using _nodes = detail::type_vector; using _can_be_null = std::false_type; + using _provided_tables = provided_tables_of; + using _required_tables = detail::make_difference_set_t, _provided_tables>; - static_assert(is_table_t::value, "lhs argument for join() has to be a table or join"); - static_assert(is_table_t::value, "rhs argument for join() has to be a table"); - static_assert(not is_join_t::value, "rhs argument for join must not be a join"); - static_assert(is_noop::value or is_on_t::value, "invalid on expression in join().on()"); + static_assert(is_cross_join_t::value, "lhs argument for join() has to be a table or join"); + static_assert(is_on_t::value, "invalid on expression in join().on()"); - static_assert(detail::is_disjunct_from, provided_tables_of>::value, - "joined tables must not be identical"); + static_assert(required_tables_of::size::value == 0, "joined tables must not depend on other tables"); + static_assert(detail::is_subset_of, provided_tables_of>::value, + "on() condition must not depend on other tables"); - static_assert(required_tables_of::size::value == 0, "joined tables must not depend on other tables"); - - template - using set_on_t = join_t; - - template - auto on(Expr... expr) -> set_on_t> + template + cross_join_t join(T t) { - static_assert(is_noop::value, "cannot call on() twice for a single join()"); - static_assert(logic::all_t::value...>::value, - "at least one argument is not an expression in on()"); - - return {_lhs, _rhs, {std::tuple{expr...}, {}}}; - } - - auto unconditionally() -> set_on_t> - { - return {_lhs, _rhs, {}}; + return {*this, t}; } template - join_t join(T t) + cross_join_t inner_join(T t) { - static_assert(not is_noop::value, "join type requires on()"); - return {*this, t, {}}; + return {*this, t}; } template - join_t inner_join(T t) + cross_join_t outer_join(T t) { - static_assert(not is_noop::value, "join type requires on()"); - return {*this, t, {}}; + return {*this, t}; } template - join_t outer_join(T t) + cross_join_t left_outer_join(T t) { - static_assert(not is_noop::value, "join type requires on()"); - return {*this, t, {}}; + return {*this, t}; } template - join_t left_outer_join(T t) + cross_join_t right_outer_join(T t) { - static_assert(not is_noop::value, "join type requires on()"); - return {*this, t, {}}; + return {*this, t}; } - template - join_t right_outer_join(T t) - { - static_assert(not is_noop::value, "join type requires on()"); - return {*this, t, {}}; - } - - Lhs _lhs; - Rhs _rhs; + CrossJoin _cross_join; On _on; }; - template - struct serializer_t> + template + struct serializer_t> { - using _serialize_check = serialize_check_of; - using T = join_t; + using _serialize_check = serialize_check_of; + using T = join_t; static Context& _(const T& t, Context& context) { - static_assert(not is_noop::value, "joined tables require on()"); - serialize(t._lhs, context); - context << JoinType::_name; - context << " JOIN "; - serialize(t._rhs, context); + serialize(t._cross_join, context); serialize(t._on, context); return context; } diff --git a/include/sqlpp11/on.h b/include/sqlpp11/on.h index dea4789d..58fb3996 100644 --- a/include/sqlpp11/on.h +++ b/include/sqlpp11/on.h @@ -28,23 +28,20 @@ #define SQLPP_ON_H #include -#include #include #include #include namespace sqlpp { - template + template struct on_t { using _traits = make_traits; - using _nodes = detail::type_vector; + using _nodes = detail::type_vector; using _is_dynamic = is_database; - static_assert(_is_dynamic::value or sizeof...(Expressions), "at least one expression argument required in on()"); - template void add(Expr expr) { @@ -69,7 +66,7 @@ namespace sqlpp void _add_impl(Expr expr, const std::false_type&); public: - std::tuple _expressions; + Expression _expression; interpretable_list_t _dynamic_expressions; }; @@ -103,8 +100,8 @@ namespace sqlpp if (sizeof...(Expressions) == 0 and t._dynamic_expressions.empty()) return context; context << " ON ("; - interpret_tuple(t._expressions, " AND ", context); - if (sizeof...(Expressions) and not t._dynamic_expressions.empty()) + serialize(t._expression, context); + if (not t._dynamic_expressions.empty()) context << " AND "; interpret_list(t._dynamic_expressions, " AND ", context); context << " )"; diff --git a/include/sqlpp11/table.h b/include/sqlpp11/table.h index 3a8f590b..029d44a6 100644 --- a/include/sqlpp11/table.h +++ b/include/sqlpp11/table.h @@ -62,33 +62,33 @@ namespace sqlpp using _alias_t = table_alias_t; template - join_t join(T t) const + cross_join_t join(T t) const { - return {*static_cast(this), t, {}}; + return {*static_cast(this), t}; } template - join_t inner_join(T t) const + cross_join_t inner_join(T t) const { - return {*static_cast(this), t, {}}; + return {*static_cast(this), t}; } template - join_t outer_join(T t) const + cross_join_t outer_join(T t) const { - return {*static_cast(this), t, {}}; + return {*static_cast(this), t}; } template - join_t left_outer_join(T t) const + cross_join_t left_outer_join(T t) const { - return {*static_cast(this), t, {}}; + return {*static_cast(this), t}; } template - join_t right_outer_join(T t) const + cross_join_t right_outer_join(T t) const { - return {*static_cast(this), t, {}}; + return {*static_cast(this), t}; } template diff --git a/include/sqlpp11/table_alias.h b/include/sqlpp11/table_alias.h index b89b5e09..a86d7915 100644 --- a/include/sqlpp11/table_alias.h +++ b/include/sqlpp11/table_alias.h @@ -61,33 +61,33 @@ namespace sqlpp } template - join_t join(T t) const + cross_join_t join(T t) const { - return {*this, t, {}}; + return {*this, t}; } template - join_t inner_join(T t) const + cross_join_t inner_join(T t) const { - return {*this, t, {}}; + return {*this, t}; } template - join_t outer_join(T t) const + cross_join_t outer_join(T t) const { - return {*this, t, {}}; + return {*this, t}; } template - join_t left_outer_join(T t) const + cross_join_t left_outer_join(T t) const { - return {*this, t, {}}; + return {*this, t}; } template - join_t right_outer_join(T t) const + cross_join_t right_outer_join(T t) const { - return {*this, t, {}}; + return {*this, t}; } Table _table; diff --git a/include/sqlpp11/type_traits.h b/include/sqlpp11/type_traits.h index 1600d230..b35e8087 100644 --- a/include/sqlpp11/type_traits.h +++ b/include/sqlpp11/type_traits.h @@ -56,6 +56,12 @@ namespace sqlpp template using value_type_of = typename detail::value_type_of_impl::type; + template + struct is_not_cpp_bool_t + { + static constexpr bool value = not std::is_same::value; + }; + // data types struct boolean; template @@ -163,6 +169,7 @@ namespace sqlpp SQLPP_VALUE_TRAIT_GENERATOR(is_return_value) SQLPP_VALUE_TRAIT_GENERATOR(is_table) SQLPP_VALUE_TRAIT_GENERATOR(is_raw_table) + SQLPP_VALUE_TRAIT_GENERATOR(is_cross_join) SQLPP_VALUE_TRAIT_GENERATOR(is_join) SQLPP_VALUE_TRAIT_GENERATOR(is_dynamic_join) SQLPP_VALUE_TRAIT_GENERATOR(is_pseudo_table) @@ -374,6 +381,7 @@ namespace sqlpp using serialize_check_of = detail::get_first_if::_serialize_check...>; + SQLPP_PORTABLE_STATIC_ASSERT(assert_sqlpp_type_t, "expression is not an sqlpp type, consistency cannot be verified"); SQLPP_PORTABLE_STATIC_ASSERT(assert_run_statement_or_prepared_t, "connection cannot run something that is neither statement nor prepared statement"); SQLPP_PORTABLE_STATIC_ASSERT(assert_prepare_statement_t, @@ -382,12 +390,11 @@ namespace sqlpp template struct consistency_check { - using type = assert_run_statement_or_prepared_t; + using type = assert_sqlpp_type_t; }; template - struct consistency_check::value or is_prepared_statement_t::value>::type> + struct consistency_check> { using type = typename T::_consistency_check; }; diff --git a/include/sqlpp11/where.h b/include/sqlpp11/where.h index ae570602..d9591b9e 100644 --- a/include/sqlpp11/where.h +++ b/include/sqlpp11/where.h @@ -93,6 +93,7 @@ namespace sqlpp { static_assert(_is_dynamic::value, "where::add() can only be called for dynamic_where"); static_assert(is_expression_t::value, "invalid expression argument in where::add()"); + static_assert(is_boolean_t::value, "invalid expression argument in where::add()"); static_assert(not TableCheckRequired::value or Policies::template _no_unknown_tables::value, "expression uses tables unknown to this statement in where::add()"); static_assert(not contains_aggregate_function_t::value, @@ -217,9 +218,11 @@ namespace sqlpp SQLPP_PORTABLE_STATIC_ASSERT(assert_where_t, "calling where() or uncontionally() required"); - SQLPP_PORTABLE_STATIC_ASSERT(assert_where_boolean_expression_t, - "at least one argument is not a sqlpp::boolean expression in where(). Please use " + SQLPP_PORTABLE_STATIC_ASSERT(assert_where_not_cpp_bool_t, + "where() argument has to be an sqlpp boolean expression. Please use " ".unconditionally() instead of .where(true), or sqlpp::value(bool)"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_where_boolean_expression_t, + "where() argument has to be an sqlpp boolean expression."); SQLPP_PORTABLE_STATIC_ASSERT(assert_where_no_aggregate_functions_t, "at least one aggregate function used in where()"); SQLPP_PORTABLE_STATIC_ASSERT(assert_where_static_count_args_t, "missing argument in where()"); @@ -230,6 +233,8 @@ namespace sqlpp // https://connect.microsoft.com/VisualStudio/feedback/details/2173198 // template // using check_where_t = static_combined_check_t< + // static_check_t::value...>::value, + // assert_where_not_cpp_bool_t>, // static_check_t::value...>::value, // assert_where_boolean_expressions_t>, // static_check_t::value...>::value, assert_where_boolean_expression_t>, @@ -239,6 +244,7 @@ namespace sqlpp struct check_where { using type = static_combined_check_t< + static_check_t::value...>::value, assert_where_not_cpp_bool_t>, static_check_t::type::value...>::value, assert_where_boolean_expression_t>, static_check_t::value...>::value, assert_where_boolean_expression_t>, @@ -313,14 +319,6 @@ namespace sqlpp using _database_t = typename Policies::_database_t; - // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 - // template - // using _check = logic::all_t::value...>; - template - struct _check : logic::all_t::value...> - { - }; - template using _new_statement_t = new_statement_t; diff --git a/test_static_asserts/CMakeLists.txt b/test_static_asserts/CMakeLists.txt index 234f2127..23b9ca67 100644 --- a/test_static_asserts/CMakeLists.txt +++ b/test_static_asserts/CMakeLists.txt @@ -30,6 +30,7 @@ endfunction() test_compile(aggregates) test_compile(case) +test_compile(from) test_compile(where) test_compile(insert) test_compile(date) diff --git a/test_static_asserts/from.cpp b/test_static_asserts/from.cpp new file mode 100644 index 00000000..a102e804 --- /dev/null +++ b/test_static_asserts/from.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2015-2016, 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 +#include "MockDb.h" +#include "Sample.h" +#include + +namespace +{ + constexpr auto t = test::TabBar{}; + constexpr auto f = test::TabFoo{}; + + template + void print_type_on_error(std::true_type) + { + } + + template + void print_type_on_error(std::false_type) + { + T::_print_me_; + } + + template + void from_static_check(const Expression& expression) + { + using CheckResult = sqlpp::check_from_static_t; + using ExpectedCheckResult = std::is_same; + print_type_on_error(ExpectedCheckResult{}); + static_assert(ExpectedCheckResult::value, "Unexpected check result"); + + using ReturnType = decltype(select(t.alpha).from(expression)); + using ExpectedReturnType = + sqlpp::logic::all_t::value>; + print_type_on_error(ExpectedReturnType{}); + static_assert(ExpectedReturnType::value, "Unexpected return type"); + } + + template + void from_dynamic_check(const Expression& expression) + { + static auto db = MockDb{}; + using CheckResult = sqlpp::check_from_dynamic_t; + using ExpectedCheckResult = std::is_same; + print_type_on_error(ExpectedCheckResult{}); + static_assert(ExpectedCheckResult::value, "Unexpected check result"); + + using ReturnType = decltype(dynamic_select(db, t.alpha).dynamic_from(expression)); + using ExpectedReturnType = + sqlpp::logic::all_t::value>; + print_type_on_error(ExpectedReturnType{}); + static_assert(ExpectedReturnType::value, "Unexpected return type"); + } + + void static_from() + { + // OK + from_static_check(t); + from_static_check(t.join(f).unconditionally()); + from_static_check(t.join(f).on(t.alpha > f.omega)); + + // Try a bunch of non-tables + from_static_check(7); + from_static_check(t.alpha); + from_static_check(t.beta); + from_static_check(t.gamma); + from_static_check(t.delta); + + // Try cross joins (missing condition) + from_static_check(t.join(f)); + } + + void dynamic_from() + { + // OK + from_dynamic_check(t); + from_dynamic_check(t.join(f).unconditionally()); + from_dynamic_check(t.join(f).on(t.alpha > f.omega)); + + // Try a bunch of non-tables + from_dynamic_check(7); + from_dynamic_check(t.alpha); + from_dynamic_check(t.beta); + from_dynamic_check(t.gamma); + from_dynamic_check(t.delta); + + // Try cross joins (missing condition) + from_dynamic_check(t.join(f)); + } +} + +int main(int, char* []) +{ + static_from(); + dynamic_from(); +} diff --git a/test_static_asserts/where.cpp b/test_static_asserts/where.cpp index 8f838716..5694a296 100644 --- a/test_static_asserts/where.cpp +++ b/test_static_asserts/where.cpp @@ -89,6 +89,10 @@ namespace // Try non-boolean expression where_static_check(t.alpha); + // Try builtin bool + where_static_check(true); + where_static_check(17 > 3); + // Try some other types as expressions where_static_check("true"); where_static_check(17); @@ -116,6 +120,10 @@ namespace // Try non-boolean expression where_dynamic_check(t.alpha); + // Try builtin bool + where_dynamic_check(true); + where_dynamic_check(17 > 3); + // Try some other types as expressions where_dynamic_check("true"); where_dynamic_check(17);