From f753697584ef0301db98cd1847b98d27269b8e5c Mon Sep 17 00:00:00 2001 From: rbock Date: Fri, 11 Mar 2016 09:25:36 +0100 Subject: [PATCH] Fixed outer-tables for joins, added tests --- CMakeLists.txt | 1 + include/sqlpp11/aggregate_functions/avg.h | 6 +- include/sqlpp11/join_types.h | 4 +- include/sqlpp11/pre_join.h | 3 +- test_serializer/From.cpp | 28 +++ test_types/CMakeLists.txt | 32 ++++ test_types/compare.h | 50 +++++ test_types/result_row.cpp | 212 ++++++++++++++++++++++ tests/Sample.h | 2 +- 9 files changed, 331 insertions(+), 7 deletions(-) create mode 100644 test_types/CMakeLists.txt create mode 100644 test_types/compare.h create mode 100644 test_types/result_row.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 16f12bd6..9f155e68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,7 @@ target_compile_features(sqlpp11 INTERFACE endif () add_subdirectory(tests) +add_subdirectory(test_types) add_subdirectory(test_serializer) add_subdirectory(test_static_asserts) add_subdirectory(test_constraints) diff --git a/include/sqlpp11/aggregate_functions/avg.h b/include/sqlpp11/aggregate_functions/avg.h index 4d274d46..f3c273b8 100644 --- a/include/sqlpp11/aggregate_functions/avg.h +++ b/include/sqlpp11/aggregate_functions/avg.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, @@ -111,7 +111,7 @@ namespace sqlpp { 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"); + static_assert(is_numeric_t>::value, "avg() requires a numeric value expression as argument"); return {t}; } @@ -120,7 +120,7 @@ namespace sqlpp { 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"); + static_assert(is_numeric_t>::value, "avg() requires a numeric value expression as argument"); return {t}; } } diff --git a/include/sqlpp11/join_types.h b/include/sqlpp11/join_types.h index 8b673be8..50b01ed8 100644 --- a/include/sqlpp11/join_types.h +++ b/include/sqlpp11/join_types.h @@ -49,14 +49,14 @@ namespace sqlpp struct left_outer_join_t { template - using _provided_outer_tables = detail::make_joined_set_t, provided_outer_tables_of>; + using _provided_outer_tables = detail::make_joined_set_t, provided_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>; + using _provided_outer_tables = detail::make_joined_set_t, provided_outer_tables_of>; static constexpr const char* _name = " RIGHT OUTER"; }; diff --git a/include/sqlpp11/pre_join.h b/include/sqlpp11/pre_join.h index 00cccf0e..07dc4078 100644 --- a/include/sqlpp11/pre_join.h +++ b/include/sqlpp11/pre_join.h @@ -91,6 +91,7 @@ namespace sqlpp using _traits = make_traits; using _nodes = detail::type_vector; using _can_be_null = std::false_type; + using _provided_outer_tables = typename JoinType::template _provided_outer_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"); @@ -185,7 +186,7 @@ namespace sqlpp template auto outer_join(Lhs lhs, Rhs rhs) -> typename std::conditional::value, - pre_join_t, + pre_join_t, bad_statement>::type { check_pre_join_t::_(); diff --git a/test_serializer/From.cpp b/test_serializer/From.cpp index 2827b719..a7978ea3 100644 --- a/test_serializer/From.cpp +++ b/test_serializer/From.cpp @@ -48,6 +48,14 @@ int From(int, char* []) compare(__LINE__, from(foo.cross_join(bar)), " FROM tab_foo CROSS JOIN tab_bar"); compare(__LINE__, from(foo.join(bar).on(foo.omega > bar.alpha)), " FROM tab_foo INNER JOIN tab_bar ON (tab_foo.omega>tab_bar.alpha)"); + compare(__LINE__, from(foo.inner_join(bar).on(foo.omega > bar.alpha)), + " FROM tab_foo INNER JOIN tab_bar ON (tab_foo.omega>tab_bar.alpha)"); + compare(__LINE__, from(foo.outer_join(bar).on(foo.omega > bar.alpha)), + " FROM tab_foo OUTER JOIN tab_bar ON (tab_foo.omega>tab_bar.alpha)"); + compare(__LINE__, from(foo.left_outer_join(bar).on(foo.omega > bar.alpha)), + " FROM tab_foo LEFT OUTER JOIN tab_bar ON (tab_foo.omega>tab_bar.alpha)"); + compare(__LINE__, from(foo.right_outer_join(bar).on(foo.omega > bar.alpha)), + " FROM tab_foo RIGHT OUTER JOIN tab_bar ON (tab_foo.omega>tab_bar.alpha)"); compare(__LINE__, from(aFoo.join(bFoo).on(aFoo.omega > bFoo.omega)), " FROM tab_foo AS a INNER JOIN tab_foo AS b ON (a.omega>b.omega)"); compare( @@ -62,11 +70,31 @@ int From(int, char* []) dfa.from.add(dynamic_cross_join(bar)); compare(__LINE__, dfa, " FROM tab_foo CROSS JOIN tab_bar"); } + { + auto dfa = df; + dfa.from.add(dynamic_join(bar).on(bar.alpha > foo.omega)); + compare(__LINE__, dfa, " FROM tab_foo INNER JOIN tab_bar ON (tab_bar.alpha>tab_foo.omega)"); + } { auto dfa = df; dfa.from.add(dynamic_inner_join(bar).on(bar.alpha > foo.omega)); compare(__LINE__, dfa, " FROM tab_foo INNER JOIN tab_bar ON (tab_bar.alpha>tab_foo.omega)"); } + { + auto dfa = df; + dfa.from.add(dynamic_outer_join(bar).on(bar.alpha > foo.omega)); + compare(__LINE__, dfa, " FROM tab_foo OUTER JOIN tab_bar ON (tab_bar.alpha>tab_foo.omega)"); + } + { + auto dfa = df; + dfa.from.add(dynamic_left_outer_join(bar).on(bar.alpha > foo.omega)); + compare(__LINE__, dfa, " FROM tab_foo LEFT OUTER JOIN tab_bar ON (tab_bar.alpha>tab_foo.omega)"); + } + { + auto dfa = df; + dfa.from.add(dynamic_right_outer_join(bar).on(bar.alpha > foo.omega)); + compare(__LINE__, dfa, " FROM tab_foo RIGHT OUTER JOIN tab_bar ON (tab_bar.alpha>tab_foo.omega)"); + } { auto dfa = df; dfa.from.add(dynamic_inner_join(bar).on(bar.alpha > foo.omega)); diff --git a/test_types/CMakeLists.txt b/test_types/CMakeLists.txt new file mode 100644 index 00000000..cb2b8a8b --- /dev/null +++ b/test_types/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2016-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. + +function(test_compile name) + set(target sqlpp11_${name}) + add_executable(${target} ${name}.cpp) + target_link_libraries(${target} PRIVATE sqlpp11 sqlpp11_testing) +endfunction() + +test_compile(result_row) + diff --git a/test_types/compare.h b/test_types/compare.h new file mode 100644 index 00000000..9709a44c --- /dev/null +++ b/test_types/compare.h @@ -0,0 +1,50 @@ +/* + * 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_TEST_SERIALIZER_H +#define SQLPP_TEST_SERIALIZER_H + +#include "MockDb.h" +#include + +namespace +{ + template + void compare(int lineNo, const Expression& expr, const std::string& expected) + { + MockDb::_serializer_context_t printer = {}; + + const auto result = serialize(expr, printer).str(); + + if (result != expected) + { + std::cerr << __FILE__ << " " << lineNo << '\n' << "Expected: -->|" << expected << "|<--\n" + << "Received: -->|" << result << "|<--\n"; + throw std::runtime_error("unexpected serialization result"); + } + } +} + +#endif diff --git a/test_types/result_row.cpp b/test_types/result_row.cpp new file mode 100644 index 00000000..31858a91 --- /dev/null +++ b/test_types/result_row.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2016-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 "MockDb.h" +#include "Sample.h" +#include + +namespace +{ + constexpr auto bar = test::TabBar{}; + constexpr auto foo = test::TabFoo{}; + + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(not sqlpp::can_be_null_t::value, ""); + static_assert(not sqlpp::can_be_null_t::value, ""); + const auto seven = sqlpp::value(7).as(sqlpp::alias::s); + static_assert(not sqlpp::can_be_null_t::value, ""); + + auto db = MockDb{}; + + void single_table() + { + { + // result fields are as nullable as the expressions they represent + const auto& x = db(select(bar.alpha, bar.gamma, seven).from(bar).unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(not sqlpp::can_be_null_t::value, ""); + static_assert(not sqlpp::can_be_null_t::value, ""); + } + } + + void join() + { + // Join + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(foo.join(bar).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(not sqlpp::can_be_null_t::value, "left side of (inner) join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "right side of (inner) join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(bar.join(foo).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(not sqlpp::can_be_null_t::value, "left side of (inner) join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "right side of (inner) join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + + // Inner join + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(foo.inner_join(bar).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(not sqlpp::can_be_null_t::value, "left side of inner join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "right side of inner join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(bar.inner_join(foo).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(not sqlpp::can_be_null_t::value, "left side of inner join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "right side of inner join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + + // Left outer join + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(foo.left_outer_join(bar).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(not sqlpp::can_be_null_t::value, "left side of left outer join cannot be null"); + static_assert(sqlpp::can_be_null_t::value, "right side of left outer join can be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(bar.left_outer_join(foo).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(not sqlpp::can_be_null_t::value, "left side of left outer join cannot be null"); + static_assert(sqlpp::can_be_null_t::value, "right side of left outer join can be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + + // Right outer join + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(foo.right_outer_join(bar).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(sqlpp::can_be_null_t::value, "left side of right outer join can be null"); + static_assert(not sqlpp::can_be_null_t::value, + "right side of right outer join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(bar.right_outer_join(foo).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(sqlpp::can_be_null_t::value, "left side of right outer join can be null"); + static_assert(not sqlpp::can_be_null_t::value, + "right side of right outer join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + + // Outer join + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(foo.outer_join(bar).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(sqlpp::can_be_null_t::value, "left side of outer join can be null"); + static_assert(sqlpp::can_be_null_t::value, "right side of outer join can be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + { + const auto& x = db(select(bar.alpha, foo.delta, bar.gamma, seven) + .from(bar.outer_join(foo).on(foo.omega > bar.alpha)) + .unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(sqlpp::can_be_null_t::value, "left side of outer join can be null"); + static_assert(sqlpp::can_be_null_t::value, "right side of outer join can be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + + // Cross join + { + const auto& x = + db(select(bar.alpha, foo.delta, bar.gamma, seven).from(foo.cross_join(bar)).unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(not sqlpp::can_be_null_t::value, "left side of cross join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "right side of cross join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + { + const auto& x = + db(select(bar.alpha, foo.delta, bar.gamma, seven).from(bar.cross_join(foo)).unconditionally()).front(); + static_assert(sqlpp::can_be_null_t::value, "nullable value can always be null"); + static_assert(not sqlpp::can_be_null_t::value, "left side of cross join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "right side of cross join cannot be null"); + static_assert(not sqlpp::can_be_null_t::value, "constant non-null value can not be null"); + } + } + + void aggregates() + { + { + // aggregates of nullable values + const auto a = bar.alpha; + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + const auto& x = db(select(count(a), avg(a), max(a), min(a), sum(a)).from(bar).unconditionally()).front(); + static_assert(not sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + } + { + // aggregates of nullable values + const auto o = foo.omega; + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + const auto& x = db(select(count(o), avg(o), max(o), min(o), sum(o)).from(foo).unconditionally()).front(); + static_assert(not sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + static_assert(sqlpp::can_be_null_t::value, ""); + } + } +} + +int main(int, char* []) +{ + single_table(); + join(); + aggregates(); +} diff --git a/tests/Sample.h b/tests/Sample.h index c95cd98e..0bb6dc51 100644 --- a/tests/Sample.h +++ b/tests/Sample.h @@ -29,7 +29,7 @@ namespace test } }; }; - using _traits = sqlpp::make_traits; + using _traits = sqlpp::make_traits; }; struct Epsilon {