From 5e96551f839051170f5cd4b70e99dfba72b218a1 Mon Sep 17 00:00:00 2001 From: rbock Date: Thu, 25 Feb 2016 07:57:28 +0100 Subject: [PATCH] Prevent unconditional joins, and naked bool in where() or boolean expressions - `.from(t1, t2)` produces an unconditional join if you forget to add a condition in the .where() sqlpp11 therefore now deprecates unconditional joins. - more often than not, writing something like `where(name == "doe")`, you meant to write `where(t.name == "doe")`. It is hard to find bugs when the former expression compiles because you happen to have a variable `name` in the current scope as well. sqlpp11 therefore now deprecates `.where(bool)` and disallows raw bool values boolean expression like `something and bool` wrap bools in sqlpp::value(), if you REALLY want a bool value here --- include/sqlpp11/bad_statement.h | 2 + .../data_types/boolean/expression_operators.h | 6 +- include/sqlpp11/dynamic_join.h | 120 ++++++++++++++++++ include/sqlpp11/from.h | 53 +++++--- include/sqlpp11/join.h | 37 +----- include/sqlpp11/join_types.h | 65 ++++++++++ include/sqlpp11/on.h | 23 +++- include/sqlpp11/operand_check.h | 19 +-- include/sqlpp11/table_alias.h | 34 ++++- include/sqlpp11/type_traits.h | 1 + include/sqlpp11/unconditional.h | 36 ++++++ include/sqlpp11/where.h | 98 +++++++++++++- ...onversion_operator_if_null_not_trivial.cpp | 2 +- test_static_asserts/CMakeLists.txt | 1 + test_static_asserts/aggregates.cpp | 48 +++---- test_static_asserts/unwrapped_bool.cpp | 115 +++++++++++++++++ test_static_asserts/where.cpp | 1 - tests/DateTime.cpp | 2 +- tests/Function.cpp | 6 +- tests/Interpret.cpp | 11 +- tests/Remove.cpp | 2 +- tests/Result.cpp | 8 +- tests/Select.cpp | 16 +-- tests/SelectType.cpp | 11 +- tests/Union.cpp | 18 +-- tests/Update.cpp | 8 +- tests/With.cpp | 6 +- 27 files changed, 617 insertions(+), 132 deletions(-) create mode 100644 include/sqlpp11/dynamic_join.h create mode 100644 include/sqlpp11/join_types.h create mode 100644 include/sqlpp11/unconditional.h create mode 100644 test_static_asserts/unwrapped_bool.cpp diff --git a/include/sqlpp11/bad_statement.h b/include/sqlpp11/bad_statement.h index 03635a28..c8b78a5c 100644 --- a/include/sqlpp11/bad_statement.h +++ b/include/sqlpp11/bad_statement.h @@ -31,6 +31,8 @@ namespace sqlpp { struct bad_statement { + static constexpr bool value = false; + template bad_statement(T&&...) { diff --git a/include/sqlpp11/data_types/boolean/expression_operators.h b/include/sqlpp11/data_types/boolean/expression_operators.h index 41b946bd..42e700f9 100644 --- a/include/sqlpp11/data_types/boolean/expression_operators.h +++ b/include/sqlpp11/data_types/boolean/expression_operators.h @@ -40,21 +40,21 @@ namespace sqlpp }; template - struct return_type_and> + struct return_type_and> { using check = consistent_t; using type = logical_and_t, wrap_operand_t>; }; template - struct return_type_or> + struct return_type_or> { using check = consistent_t; using type = logical_or_t, wrap_operand_t>; }; template - struct return_type_not> + struct return_type_not> { using check = consistent_t; using type = logical_not_t>; diff --git a/include/sqlpp11/dynamic_join.h b/include/sqlpp11/dynamic_join.h new file mode 100644 index 00000000..1ec70153 --- /dev/null +++ b/include/sqlpp11/dynamic_join.h @@ -0,0 +1,120 @@ +/* + * 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. + */ + +#ifndef SQLPP_DYNAMIC_JOIN_H +#define SQLPP_DYNAMIC_JOIN_H + +#include +#include +#include + +namespace sqlpp +{ + template + struct dynamic_join_t + { + using _traits = make_traits; + using _nodes = detail::type_vector; + using _can_be_null = std::false_type; + + 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(required_tables_of::size::value == 0, + "joined tables must not depend on other tables"); + + template + using set_on_t = dynamic_join_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()"); + + return {_rhs, {std::tuple{expr...}, {}}}; + } + + auto unconditionally() -> set_on_t> + { + static_assert(is_noop::value, "cannot call on() twice for a single join()"); + return {_rhs, {}}; + } + + Rhs _rhs; + On _on; + }; + + template + struct serializer_t> + { + using _serialize_check = serialize_check_of; + using T = dynamic_join_t; + + static Context& _(const T& t, Context& context) + { + static_assert(not is_noop::value, "joined tables require on()"); + context << " JOIN "; + serialize(t._rhs, context); + serialize(t._on, context); + return context; + } + }; + + template + dynamic_join_t dynamic_join(Table table) + { + return {table, {}}; + } + + template + dynamic_join_t dynamic_inner_join(Table table) + { + return {table, {}}; + } + + template + dynamic_join_t outer_join(Table table) + { + return {table, {}}; + } + + template + dynamic_join_t left_outer_join(Table table) + { + return {table, {}}; + } + + template + dynamic_join_t right_outer_join(Table table) + { + return {table, {}}; + } +} + +#endif diff --git a/include/sqlpp11/from.h b/include/sqlpp11/from.h index f6339819..35c77eaa 100644 --- a/include/sqlpp11/from.h +++ b/include/sqlpp11/from.h @@ -35,9 +35,16 @@ #include #include #include +#include namespace sqlpp { +#ifdef SQLPP_ALLOW_UNCONDITIONAL_JOIN + constexpr bool allow_unconditional_from = 1; +#else + constexpr bool allow_unconditional_from = 0; +#endif + // FROM DATA template struct from_data_t @@ -77,35 +84,40 @@ namespace sqlpp { } - template - void add(Table table) + template + void add(DynamicJoin dynamicJoin) { static_assert(_is_dynamic::value, "from::add() must not be called for static from()"); - static_assert(is_table_t::value, "invalid table argument in from::add()"); + static_assert( + is_dynamic_join_t::value or (allow_unconditional_from and is_table_t::value), + "invalid argument in from::add(), or #define ALLOW_UNCONDITIONAL_JOIN " + "for a grace period of using tables here"); using _known_tables = detail::make_joined_set_t...>; // Hint: Joins contain more than one table // workaround for msvc bug https://connect.microsoft.com/VisualStudio/feedback/details/2173198 // using _known_table_names = detail::transform_set_t; using _known_table_names = detail::make_name_of_set_t<_known_tables>; - static_assert(not detail::is_element_of::value, + using _joined_tables = provided_tables_of; + using _joined_table_names = detail::make_name_of_set_t<_joined_tables>; + static_assert(detail::is_disjunct_from<_joined_table_names, _known_table_names>::value, "Must not use the same table name twice in from()"); - using _serialize_check = sqlpp::serialize_check_t; + using _serialize_check = sqlpp::serialize_check_t; _serialize_check::_(); - using ok = logic::all_t<_is_dynamic::value, is_table_t
::value, _serialize_check::type::value>; + using ok = logic::all_t<_is_dynamic::value, is_table_t::value, _serialize_check::type::value>; - _add_impl(table, ok()); // dispatch to prevent compile messages after the static_assert + _add_impl(dynamicJoin, ok()); // dispatch to prevent compile messages after the static_assert } private: - template - void _add_impl(Table table, const std::true_type&) + template + void _add_impl(DynamicJoin dynamicJoin, const std::true_type&) { - return _data._dynamic_tables.emplace_back(from_table(table)); + return _data._dynamic_tables.emplace_back(from_table(dynamicJoin)); } - template - void _add_impl(Table table, const std::false_type&); + template + void _add_impl(DynamicJoin dynamicJoin, const std::false_type&); public: _data_t _data; @@ -210,12 +222,15 @@ namespace sqlpp using _consistency_check = consistent_t; - template - auto from(Tables... tables) const -> _new_statement_t<_check, from_t...>> + template + auto from(Table table, Tables... tables) const + -> _new_statement_t<_check, from_t, from_table_t...>> { - static_assert(_check::value, "at least one argument is not a table or join in from()"); - static_assert(sizeof...(Tables), "at least one table or join argument required in from()"); - return _from_impl(_check{}, tables...); + static_assert(_check::value, "at least one argument is not a table or join in from()"); + static_assert(sizeof...(Tables) == 0 or ::sqlpp::allow_unconditional_from, + "unconditional join is deprecated, please use explicit joins or #define ALLOW_UNCONDITIONAL_JOIN " + "for a grace period"); + return _from_impl(_check{}, table, tables...); } template @@ -225,6 +240,10 @@ namespace sqlpp static_assert(not std::is_same<_database_t, void>::value, "dynamic_from must not be called in a static statement"); static_assert(_check::value, "at least one argument is not a table or join in from()"); + static_assert( + sizeof...(Tables) == 1 or ::sqlpp::allow_unconditional_from, + "unconditional join is deprecated, please use explicit joins or #define SQLPP_ALLOW_UNCONDITIONAL_JOIN " + "for a grace period"); return _from_impl<_database_t>(_check{}, tables...); } diff --git a/include/sqlpp11/join.h b/include/sqlpp11/join.h index 5db3f7a1..67e8643f 100644 --- a/include/sqlpp11/join.h +++ b/include/sqlpp11/join.h @@ -27,42 +27,12 @@ #ifndef SQLPP_JOIN_H #define SQLPP_JOIN_H -#include +#include #include #include namespace sqlpp { - struct inner_join_t - { - template - using _provided_outer_tables = - detail::make_joined_set_t, provided_outer_tables_of>; - - static constexpr const char* _name = " INNER "; - }; - struct outer_join_t - { - template - using _provided_outer_tables = detail::make_joined_set_t, provided_tables_of>; - - static constexpr const char* _name = " OUTER "; - }; - struct left_outer_join_t - { - template - using _provided_outer_tables = detail::make_joined_set_t, provided_outer_tables_of>; - - static constexpr const char* _name = " LEFT OUTER "; - }; - struct right_outer_join_t - { - template - using _provided_outer_tables = detail::make_joined_set_t, provided_tables_of>; - - static constexpr const char* _name = " RIGHT OUTER "; - }; - template struct join_t { @@ -93,6 +63,11 @@ namespace sqlpp return {_lhs, _rhs, {std::tuple{expr...}, {}}}; } + auto unconditionally() -> set_on_t> + { + return {_lhs, _rhs, {}}; + } + template join_t join(T t) { diff --git a/include/sqlpp11/join_types.h b/include/sqlpp11/join_types.h new file mode 100644 index 00000000..1f670b7c --- /dev/null +++ b/include/sqlpp11/join_types.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef SQLPP_JOIN_TYPES_H +#define SQLPP_JOIN_TYPES_H + +#include + +namespace sqlpp +{ + struct inner_join_t + { + template + using _provided_outer_tables = + detail::make_joined_set_t, provided_outer_tables_of>; + + static constexpr const char* _name = " INNER "; + }; + struct outer_join_t + { + template + using _provided_outer_tables = detail::make_joined_set_t, provided_tables_of>; + + static constexpr const char* _name = " OUTER "; + }; + struct left_outer_join_t + { + template + using _provided_outer_tables = detail::make_joined_set_t, provided_outer_tables_of>; + + static constexpr const char* _name = " LEFT OUTER "; + }; + struct right_outer_join_t + { + template + using _provided_outer_tables = detail::make_joined_set_t, provided_tables_of>; + + static constexpr const char* _name = " RIGHT OUTER "; + }; +} + +#endif diff --git a/include/sqlpp11/on.h b/include/sqlpp11/on.h index bccdc3e8..dea4789d 100644 --- a/include/sqlpp11/on.h +++ b/include/sqlpp11/on.h @@ -30,6 +30,7 @@ #include #include #include +#include #include namespace sqlpp @@ -72,6 +73,25 @@ namespace sqlpp interpretable_list_t _dynamic_expressions; }; + template <> + struct on_t + { + using _traits = make_traits; + using _nodes = detail::type_vector<>; + }; + + template + struct serializer_t> + { + using _serialize_check = consistent_t; + using T = on_t; + + static Context& _(const T&, Context& context) + { + return context; + } + }; + template struct serializer_t> { @@ -82,11 +102,12 @@ namespace sqlpp { if (sizeof...(Expressions) == 0 and t._dynamic_expressions.empty()) return context; - context << " ON "; + context << " ON ("; interpret_tuple(t._expressions, " AND ", context); if (sizeof...(Expressions) and not t._dynamic_expressions.empty()) context << " AND "; interpret_list(t._dynamic_expressions, " AND ", context); + context << " )"; return context; } }; diff --git a/include/sqlpp11/operand_check.h b/include/sqlpp11/operand_check.h index 45df1cfb..1de5abec 100644 --- a/include/sqlpp11/operand_check.h +++ b/include/sqlpp11/operand_check.h @@ -38,13 +38,16 @@ namespace sqlpp }; template class Pred> - struct unary_operand_check>::value>> + struct unary_operand_check::value>> { using type = void; }; template class Pred> - using unary_operand_check_t = typename unary_operand_check::type; + using unary_operand_check_t = typename unary_operand_check, Pred>::type; + + template class Pred> + using unwrapped_unary_operand_check_t = typename unary_operand_check::type; template class LPred, @@ -56,17 +59,17 @@ namespace sqlpp }; template class LPred, typename R, template class RPred> - struct binary_operand_check>::value and RPred>::value>> + struct binary_operand_check::value and RPred::value>> { using type = void; }; template class LPred, typename R, template class RPred> - using binary_operand_check_t = typename binary_operand_check::type; + using binary_operand_check_t = + typename binary_operand_check, LPred, wrap_operand_t, RPred>::type; + + template class LPred, typename R, template class RPred> + using unwrapped_binary_operand_check_t = typename binary_operand_check::type; } #endif diff --git a/include/sqlpp11/table_alias.h b/include/sqlpp11/table_alias.h index af73784a..b89b5e09 100644 --- a/include/sqlpp11/table_alias.h +++ b/include/sqlpp11/table_alias.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2013-2016, Roland Bock * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,6 @@ namespace sqlpp template struct table_alias_t : public ColumnSpec::_alias_t::template _member_t>... { - // FIXME: Need to add join functionality using _traits = make_traits, tag::is_table, tag::is_alias, @@ -60,6 +60,36 @@ namespace sqlpp { } + template + join_t join(T t) const + { + return {*this, t, {}}; + } + + template + join_t inner_join(T t) const + { + return {*this, t, {}}; + } + + template + join_t outer_join(T t) const + { + return {*this, t, {}}; + } + + template + join_t left_outer_join(T t) const + { + return {*this, t, {}}; + } + + template + join_t right_outer_join(T t) const + { + return {*this, t, {}}; + } + Table _table; }; diff --git a/include/sqlpp11/type_traits.h b/include/sqlpp11/type_traits.h index 1b413161..1600d230 100644 --- a/include/sqlpp11/type_traits.h +++ b/include/sqlpp11/type_traits.h @@ -164,6 +164,7 @@ namespace sqlpp SQLPP_VALUE_TRAIT_GENERATOR(is_table) SQLPP_VALUE_TRAIT_GENERATOR(is_raw_table) SQLPP_VALUE_TRAIT_GENERATOR(is_join) + SQLPP_VALUE_TRAIT_GENERATOR(is_dynamic_join) SQLPP_VALUE_TRAIT_GENERATOR(is_pseudo_table) SQLPP_VALUE_TRAIT_GENERATOR(is_column) SQLPP_VALUE_TRAIT_GENERATOR(is_select) diff --git a/include/sqlpp11/unconditional.h b/include/sqlpp11/unconditional.h new file mode 100644 index 00000000..ac4d9349 --- /dev/null +++ b/include/sqlpp11/unconditional.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef SQLPP_UNCONDITIONAL_H +#define SQLPP_UNCONDITIONAL_H + +namespace sqlpp +{ + struct unconditional_t + { + }; +} +#endif diff --git a/include/sqlpp11/where.h b/include/sqlpp11/where.h index 448b2fbf..4327e3ff 100644 --- a/include/sqlpp11/where.h +++ b/include/sqlpp11/where.h @@ -33,6 +33,7 @@ #include #include #include +#include #include namespace sqlpp @@ -153,6 +154,7 @@ namespace sqlpp }; }; +#ifdef SQLPP_ALLOW_NAKED_BOOL_EXPRESSION template <> struct where_data_t { @@ -214,8 +216,70 @@ namespace sqlpp using _consistency_check = consistent_t; }; }; +#endif - SQLPP_PORTABLE_STATIC_ASSERT(assert_where_t, "where expression required, e.g. where(true)"); + template <> + struct where_data_t + { + }; + + // WHERE() UNCONDITIONALLY + template <> + struct where_t + { + using _traits = make_traits; + using _nodes = detail::type_vector<>; + + // Data + using _data_t = where_data_t; + + // Member implementation with data and methods + template + struct _impl_t + { + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + _impl_t() = default; + _impl_t(const _data_t& data) : _data(data) + { + } + + _data_t _data; + }; + + // Base template to be inherited by the statement + template + struct _base_t + { + using _data_t = where_data_t; + + // workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2173269 + template + _base_t(Args&&... args) + : where{std::forward(args)...} + { + } + + _impl_t where; + _impl_t& operator()() + { + return where; + } + const _impl_t& operator()() const + { + return where; + } + + template + static auto _get_member(T t) -> decltype(t.where) + { + return t.where; + } + + using _consistency_check = consistent_t; + }; + }; + + SQLPP_PORTABLE_STATIC_ASSERT(assert_where_t, "calling where() or uncontionally() required"); SQLPP_PORTABLE_STATIC_ASSERT(assert_where_expressions_t, "at least one argument is not a boolean expression in where()"); @@ -328,10 +392,28 @@ namespace sqlpp assert_where_t, consistent_t>::type; +#ifdef SQLPP_ALLOW_NAKED_BOOL_EXPRESSION + template auto where(bool b) const -> _new_statement_t> { return {static_cast&>(*this), where_data_t{b}}; } +#else + template + auto where(bool b) const -> bad_statement + { + static_assert( + wrong_t::value, + "where(bool) is deprecated, please use unconditionally() or #define SQLPP_ALLOW_NAKED_BOOL_EXPRESSION " + "for a grace period"); + return {static_cast&>(*this), where_data_t{b}}; + } +#endif + + auto unconditionally() const -> _new_statement_t> + { + return {static_cast&>(*this), where_data_t{}}; + } template auto where(Expressions... expressions) const @@ -387,6 +469,7 @@ namespace sqlpp } }; +#ifdef SQLPP_ALLOW_NAKED_BOOL_EXPRESSION template struct serializer_t> { @@ -400,6 +483,19 @@ namespace sqlpp return context; } }; +#endif + + template + struct serializer_t> + { + using _serialize_check = consistent_t; + using T = where_data_t; + + static Context& _(const T&, Context& context) + { + return context; + } + }; template auto where(T&&... t) -> decltype(statement_t>().where(std::forward(t)...)) diff --git a/test_constraints/no_conversion_operator_if_null_not_trivial.cpp b/test_constraints/no_conversion_operator_if_null_not_trivial.cpp index 091c5759..a74400f3 100644 --- a/test_constraints/no_conversion_operator_if_null_not_trivial.cpp +++ b/test_constraints/no_conversion_operator_if_null_not_trivial.cpp @@ -36,7 +36,7 @@ int main() static_assert(sqlpp::can_be_null_t::value, "t.alpha can be null"); static_assert(not sqlpp::null_is_trivial_value_t::value, "t.alpha does not say null_is_trivial"); - for (const auto& row : edb(select(all_of(t)).from(t).where(true))) + for (const auto& row : edb(select(all_of(t)).from(t).unconditionally())) { static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); static_assert(not sqlpp::null_is_trivial_value_t::value, diff --git a/test_static_asserts/CMakeLists.txt b/test_static_asserts/CMakeLists.txt index 54d9b158..234f2127 100644 --- a/test_static_asserts/CMakeLists.txt +++ b/test_static_asserts/CMakeLists.txt @@ -34,4 +34,5 @@ test_compile(where) test_compile(insert) test_compile(date) test_compile(date_time) +test_compile(unwrapped_bool) diff --git a/test_static_asserts/aggregates.cpp b/test_static_asserts/aggregates.cpp index 929bd843..f685f2ec 100644 --- a/test_static_asserts/aggregates.cpp +++ b/test_static_asserts/aggregates.cpp @@ -75,51 +75,53 @@ namespace // If there is no group_by, we can select whatever we want void no_group_by() { - 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)); + run_check(select(all_of(t)).from(t).unconditionally()); + run_check(select(t.alpha).from(t).unconditionally()); + run_check(select(count(t.alpha)).from(t).unconditionally()); } // If there is a dynamic group_by, we can still select whatever we want // because there is no way of knowing which expressions might have been added dynamically void dynamic_group_by() { - 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)); + run_check(select(all_of(t)).from(t).unconditionally()); + run_check(select(t.alpha).from(t).unconditionally()); + run_check(select(count(t.alpha)).from(t).unconditionally()); } // 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 void static_group_by_ok() { - 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(t.alpha).from(t).unconditionally().group_by(t.alpha)); + run_check(select((t.alpha + 42).as(whatever)).from(t).unconditionally().group_by(t.alpha)); + run_check(select((t.alpha + 42).as(whatever)).from(t).unconditionally().group_by(t.alpha, t.alpha + t.delta * 17)); + run_check(select((t.alpha + t.delta * 17).as(whatever)) + .from(t) + .unconditionally() + .group_by(t.alpha, t.alpha + t.delta * 17)); + run_check(select((t.beta + "fortytwo").as(whatever)).from(t).unconditionally().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(avg(t.alpha)).from(t).unconditionally().group_by(t.beta)); + run_check(select(count(t.alpha)).from(t).unconditionally().group_by(t.beta)); + run_check(select(max(t.alpha)).from(t).unconditionally().group_by(t.beta)); + run_check(select(min(t.alpha)).from(t).unconditionally().group_by(t.beta)); + run_check(select(sum(t.alpha)).from(t).unconditionally().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((t.alpha + count(t.delta)).as(whatever)).from(t).unconditionally().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)); + run_check(select(sqlpp::value(1).as(whatever)).from(t).unconditionally().group_by(t.alpha)); + run_check(select(sqlpp::value("whatever").as(whatever)).from(t).unconditionally().group_by(t.alpha)); } // Failures with static group_by and selected non-aggregates or incorrect aggregates void static_group_by_nok() { - run_check(select(t.beta).from(t).where(true).group_by(t.alpha)); + run_check(select(t.beta).from(t).unconditionally().group_by(t.alpha)); run_check( - select((t.alpha + t.delta).as(whatever)).from(t).where(true).group_by(t.alpha)); + select((t.alpha + t.delta).as(whatever)).from(t).unconditionally().group_by(t.alpha)); run_check( - select((t.alpha + t.delta).as(whatever)).from(t).where(true).group_by(t.alpha, t.alpha + t.delta * 17)); + select((t.alpha + t.delta).as(whatever)).from(t).unconditionally().group_by(t.alpha, t.alpha + t.delta * 17)); } } diff --git a/test_static_asserts/unwrapped_bool.cpp b/test_static_asserts/unwrapped_bool.cpp new file mode 100644 index 00000000..431a5ea8 --- /dev/null +++ b/test_static_asserts/unwrapped_bool.cpp @@ -0,0 +1,115 @@ +/* + * 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{}; + + template + struct wrap + { + }; + + template + void print_type_on_error(std::true_type) + { + } + + template + void print_type_on_error(std::false_type) + { + wrap::_print_me_; + } + + template + void and_check(const Lhs& lhs, const Rhs& rhs) + { + using ReturnType = decltype(lhs and rhs); + using ExpectedReturnType = + sqlpp::logic::all_t>::value>; + print_type_on_error(ExpectedReturnType{}); + static_assert(ExpectedReturnType::value, "Unexpected return type"); + } + + template + void or_check(const Lhs& lhs, const Rhs& rhs) + { + using ReturnType = decltype(lhs or rhs); + using ExpectedReturnType = + sqlpp::logic::all_t>::value>; + print_type_on_error(ExpectedReturnType{}); + static_assert(ExpectedReturnType::value, "Unexpected return type"); + } + + template + void not_check(const Lhs& lhs) + { + using ReturnType = decltype(not lhs); + using ExpectedReturnType = + sqlpp::logic::all_t>::value>; + print_type_on_error(ExpectedReturnType{}); + static_assert(ExpectedReturnType::value, "Unexpected return type"); + } + + template + void where_check(const Condition& condition) + { + using ReturnType = decltype(sqlpp::where(condition)); + using ExpectedReturnType = + sqlpp::logic::all_t::value>; + print_type_on_error(ExpectedReturnType{}); + static_assert(ExpectedReturnType::value, "Unexpected return type"); + } + + void boolean() + { + and_check(t.gamma, t.gamma); + and_check(t.gamma, true); + // and_check(true, t.gamma); // Cannot currently do that + + or_check(t.gamma, t.gamma); + or_check(t.gamma, true); + // or_check(true, t.gamma); // Cannot currently do that + + not_check(t.gamma); + } + + void where() + { + where_check(t.gamma); + where_check(true); + } +} + +int main(int, char* []) +{ + boolean(); + where(); +} diff --git a/test_static_asserts/where.cpp b/test_static_asserts/where.cpp index 9fb5d9b3..4783ceb7 100644 --- a/test_static_asserts/where.cpp +++ b/test_static_asserts/where.cpp @@ -31,7 +31,6 @@ namespace { constexpr auto t = test::TabBar{}; - constexpr auto f = test::TabFoo{}; template void print_type_on_error(std::true_type) diff --git a/tests/DateTime.cpp b/tests/DateTime.cpp index b244bed2..c0b77c33 100644 --- a/tests/DateTime.cpp +++ b/tests/DateTime.cpp @@ -40,7 +40,7 @@ int DateTime(int, char* []) { std::cout << row.now; } - for (const auto& row : db(select(all_of(t)).from(t).where(true))) + for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { std::cout << row.colDayPoint; std::cout << row.colTimePoint; diff --git a/tests/Function.cpp b/tests/Function.cpp index 7f3e3a0d..e31fcd5c 100644 --- a/tests/Function.cpp +++ b/tests/Function.cpp @@ -197,7 +197,7 @@ int Function(int, char* []) static_assert(not sqlpp::is_numeric_t::value, "type requirement"); static_assert(not sqlpp::is_text_t::value, "type requirement"); - if (false and db(select(exists(select(t.alpha).from(t).where(true)))).front().exists) + if (false and db(select(exists(select(t.alpha).from(t).unconditionally()))).front().exists) { /* do something */ } } @@ -286,7 +286,7 @@ int Function(int, char* []) static_assert(sqlpp::is_integral_t::value, "type requirement"); static_assert(not sqlpp::is_floating_point_t::value, "type requirement"); - if (false and db(select(count(t.alpha)).from(t).where(true)).front().count > 0) + if (false and db(select(count(t.alpha)).from(t).unconditionally()).front().count > 0) { /* do something */ } } @@ -444,7 +444,7 @@ int Function(int, char* []) static_assert(std::is_same>::value, "text values are accepted and wrapped"); - for (const auto& row : db(select(all_of(t)).from(t).where(true))) + for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { static_assert(std::is_same::type>>::value, diff --git a/tests/Interpret.cpp b/tests/Interpret.cpp index 9b9fffe2..24841870 100644 --- a/tests/Interpret.cpp +++ b/tests/Interpret.cpp @@ -178,10 +178,9 @@ int Interpret(int, char* []) serialize(avg(sqlpp::distinct, t.alpha - 7), printer).str(); serialize(sum(sqlpp::distinct, t.alpha + 7), printer).str(); - serialize(select(all_of(t)).from(t).where(true), printer).str(); - serialize(select(all_of(t)).from(t).where(false), printer).str(); + serialize(select(all_of(t)).from(t).unconditionally(), printer).str(); - for (const auto& row : db(select(all_of(t)).from(t).where(true))) + for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { serialize(row.alpha, printer); serialize(row.beta, printer); @@ -201,8 +200,8 @@ int Interpret(int, char* []) std::cerr << serialize(x, printer).str() << std::endl; printer.reset(); - std::cerr << serialize(select(all_of(t)).from(t).where(t.alpha.in(select(f.epsilon).from(f).where(true))), printer) - .str() << std::endl; + std::cerr << serialize(select(all_of(t)).from(t).where(t.alpha.in(select(f.epsilon).from(f).unconditionally())), + printer).str() << std::endl; printer.reset(); std::cerr << serialize(select(all_of(t)).from(t).where(t.alpha.in()), printer).str() << std::endl; @@ -214,7 +213,7 @@ int Interpret(int, char* []) auto s = schema_qualified_table(schema, t).as(sqlpp::alias::x); printer.reset(); - std::cerr << serialize(select(all_of(s)).from(s).where(true), printer).str() << std::endl; + std::cerr << serialize(select(all_of(s)).from(s).unconditionally(), printer).str() << std::endl; printer.reset(); std::cerr << serialize(sqlpp::case_when(true).then(t.alpha).else_(t.alpha + 1).as(t.beta), printer).str() diff --git a/tests/Remove.cpp b/tests/Remove.cpp index d0461b75..b1442c09 100644 --- a/tests/Remove.cpp +++ b/tests/Remove.cpp @@ -60,7 +60,7 @@ int Remove(int, char* []) printer.reset(); std::cerr << serialize(r, printer).str() << std::endl; printer.reset(); - std::cerr << serialize(remove_from(t).where(true), printer).str() << std::endl; + std::cerr << serialize(remove_from(t).unconditionally(), printer).str() << std::endl; db(r); diff --git a/tests/Result.cpp b/tests/Result.cpp index c3219be8..aac0f0e8 100644 --- a/tests/Result.cpp +++ b/tests/Result.cpp @@ -42,7 +42,7 @@ int Result(int, char* []) static_assert(not sqlpp::null_is_trivial_value_t::value, "t.alpha does not say null_is_trivial"); // Using a non-enforcing db - for (const auto& row : db(select(all_of(t), t.beta.like("")).from(t).where(true))) + for (const auto& row : db(select(all_of(t), t.beta.like("")).from(t).unconditionally())) { static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); static_assert(sqlpp::null_is_trivial_value_t::value, "row.alpha interprets null_is_trivial"); @@ -64,14 +64,14 @@ int Result(int, char* []) } sqlpp::select((t.alpha + 1).as(t.alpha)).flags(sqlpp::all).from(t); - for (const auto& row : db(select(all_of(t)).from(t).where(true))) + for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); static_assert(sqlpp::null_is_trivial_value_t::value, "row.alpha interprets null_is_trivial"); } // Using a non-enforcing db - for (const auto& row : edb(select(all_of(t)).from(t).where(true))) + for (const auto& row : edb(select(all_of(t)).from(t).unconditionally())) { static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); static_assert(not sqlpp::null_is_trivial_value_t::value, @@ -79,7 +79,7 @@ int Result(int, char* []) } sqlpp::select((t.alpha + 1).as(t.alpha)).flags(sqlpp::all).from(t); - for (const auto& row : edb(select(all_of(t)).from(t).where(true))) + for (const auto& row : edb(select(all_of(t)).from(t).unconditionally())) { static_assert(sqlpp::can_be_null_t::value, "row.alpha can be null"); static_assert(not sqlpp::null_is_trivial_value_t::value, diff --git a/tests/Select.cpp b/tests/Select.cpp index db7c7172..ad85898e 100644 --- a/tests/Select.cpp +++ b/tests/Select.cpp @@ -35,7 +35,7 @@ template int64_t getColumn(Db&& db, const Column& column) { - auto result = db(select(column.as(sqlpp::alias::a)).from(column.table()).where(true)); + auto result = db(select(column.as(sqlpp::alias::a)).from(column.table()).unconditionally()); if (not result.empty()) return result.front().a; else @@ -59,14 +59,14 @@ int Select(int, char* []) std::cout << row.a << std::endl; } - for (const auto& row : db(select(all_of(t)).from(t).where(true))) + for (const auto& row : db(select(all_of(t)).from(t).unconditionally())) { int64_t a = row.alpha; const std::string b = row.beta; std::cout << a << ", " << b << std::endl; } - for (const auto& row : db(select(all_of(t).as(t)).from(t).where(true))) + for (const auto& row : db(select(all_of(t).as(t)).from(t).unconditionally())) { int64_t a = row.tabBar.alpha; const std::string b = row.tabBar.beta; @@ -82,19 +82,19 @@ int Select(int, char* []) } for (const auto& row : - db(select(all_of(t), all_of(f)).from(t.join(f).on(t.alpha > f.omega and not t.gamma)).where(true))) + db(select(all_of(t), all_of(f)).from(t.join(f).on(t.alpha > f.omega and not t.gamma)).unconditionally())) { std::cout << row.alpha << std::endl; } for (const auto& row : db(select(all_of(t), all_of(f)) .from(t.join(f).on(t.alpha > f.omega).join(tab_a).on(t.alpha == tab_a.omega)) - .where(true))) + .unconditionally())) { std::cout << row.alpha << std::endl; } - for (const auto& row : db(select(count(t.alpha), avg(t.alpha)).from(t).where(true))) + for (const auto& row : db(select(count(t.alpha), avg(t.alpha)).from(t).unconditionally())) { std::cout << row.count << std::endl; } @@ -146,7 +146,7 @@ int Select(int, char* []) .dynamic_offset(); s.select_flags.add(sqlpp::distinct); s.selected_columns.add(f.omega); - s.from.add(f); + s.from.add(dynamic_join(f).unconditionally()); s.where.add(t.alpha > 7); s.having.add(t.alpha > 7); s.limit.set(3); @@ -165,7 +165,7 @@ int Select(int, char* []) select(sqlpp::value(7).as(t.alpha)); for (const auto& row : - db(select(sqlpp::case_when(true).then(sqlpp::null).else_(sqlpp::null).as(t.beta)).from(t).where(true))) + db(select(sqlpp::case_when(true).then(sqlpp::null).else_(sqlpp::null).as(t.beta)).from(t).unconditionally())) { std::cerr << row.beta << std::endl; } diff --git a/tests/SelectType.cpp b/tests/SelectType.cpp index d516e21d..b733a636 100644 --- a/tests/SelectType.cpp +++ b/tests/SelectType.cpp @@ -298,7 +298,7 @@ int SelectType(int, char* []) t.alpha.as(alias::a) // index 12 ) .from(t) - .where(true)); // next index is 13 + .unconditionally()); // next index is 13 using ResultRow = typename Select::_result_methods_t