diff --git a/include/sqlpp11/core/basic/join.h b/include/sqlpp11/core/basic/join.h index 75e1f12a..f48856d5 100644 --- a/include/sqlpp11/core/basic/join.h +++ b/include/sqlpp11/core/basic/join.h @@ -67,10 +67,8 @@ namespace sqlpp template struct provided_static_tables_of> { - using type = typename std::conditional< - is_dynamic::value, - provided_static_tables_of_t, - detail::type_vector_cat_t, provided_static_tables_of_t>>::type; + using type = + detail::type_vector_cat_t, provided_static_tables_of_t>; }; template @@ -79,10 +77,10 @@ namespace sqlpp using type = detail::type_vector_cat_t< typename std::conditional::contains::value, provided_tables_of_t, - detail::type_vector<>>::type, + provided_optional_tables_of_t>::type, typename std::conditional::contains::value, provided_tables_of_t, - detail::type_vector<>>::type>; + provided_optional_tables_of_t>::type>; }; #warning: Need to make extra sure that ON does not require on extra tables. diff --git a/include/sqlpp11/core/basic/schema.h b/include/sqlpp11/core/basic/schema.h index f043dcef..50117e7b 100644 --- a/include/sqlpp11/core/basic/schema.h +++ b/include/sqlpp11/core/basic/schema.h @@ -41,7 +41,7 @@ namespace sqlpp return name_to_sql_string(context, t._name); } - auto schema(std::string name) -> schema_t + inline auto schema(std::string name) -> schema_t { return schema_t{std::move(name)}; }; diff --git a/include/sqlpp11/core/basic/table.h b/include/sqlpp11/core/basic/table.h index 86162edf..d7899ab0 100644 --- a/include/sqlpp11/core/basic/table.h +++ b/include/sqlpp11/core/basic/table.h @@ -64,11 +64,6 @@ namespace sqlpp using type = sqlpp::detail::type_vector>; }; - template - struct provided_static_tables_of> : public provided_tables_of> - { - }; - template auto to_sql_string(Context& context, const table_t& /*unused*/) -> std::string { diff --git a/include/sqlpp11/core/basic/table_as.h b/include/sqlpp11/core/basic/table_as.h index 8c187c24..fd43aeb8 100644 --- a/include/sqlpp11/core/basic/table_as.h +++ b/include/sqlpp11/core/basic/table_as.h @@ -59,16 +59,6 @@ namespace sqlpp using type = sqlpp::detail::type_vector>; }; - template - struct provided_static_tables_of> : public provided_tables_of> - { - }; - - template - struct provided_optional_tables_of> : public provided_tables_of> - { - }; - template auto to_sql_string(Context& context, const table_as_t&) -> std::string { diff --git a/include/sqlpp11/core/clause/cte.h b/include/sqlpp11/core/clause/cte.h index 607e4d96..33142cde 100644 --- a/include/sqlpp11/core/clause/cte.h +++ b/include/sqlpp11/core/clause/cte.h @@ -289,11 +289,6 @@ namespace sqlpp using type = sqlpp::detail::type_vector>; }; - template - struct provided_static_tables_of> : public provided_tables_of> - { - }; - #warning: Should enough if with_t provides ctes? template struct provided_ctes_of> @@ -301,11 +296,6 @@ namespace sqlpp using type = detail::type_vector>; }; - template - struct provided_static_ctes_of> : public provided_ctes_of> - { - }; - template auto to_sql_string(Context& context, const cte_t& t) -> std::string { @@ -341,11 +331,6 @@ namespace sqlpp using type = sqlpp::detail::type_vector>; }; - template - struct provided_static_tables_of> : public provided_tables_of> - { - }; - template struct required_ctes_of> { diff --git a/include/sqlpp11/core/clause/from.h b/include/sqlpp11/core/clause/from.h index 141d2fad..b722c266 100644 --- a/include/sqlpp11/core/clause/from.h +++ b/include/sqlpp11/core/clause/from.h @@ -71,6 +71,16 @@ namespace sqlpp using type = detail::type_vector; }; + template + struct provided_tables_of> : public provided_tables_of
+ { + }; + + template + struct provided_optional_tables_of> : public provided_optional_tables_of
+ { + }; + SQLPP_PORTABLE_STATIC_ASSERT( assert_from_not_pre_join_t, "from() argument is a pre join, please use an explicit on() condition or unconditionally()"); diff --git a/include/sqlpp11/core/clause/into.h b/include/sqlpp11/core/clause/into.h index f8065b87..6ed087d1 100644 --- a/include/sqlpp11/core/clause/into.h +++ b/include/sqlpp11/core/clause/into.h @@ -77,6 +77,11 @@ namespace sqlpp using type = detail::type_vector
; }; + template + struct provided_tables_of> : public provided_tables_of
+ { + }; + SQLPP_PORTABLE_STATIC_ASSERT(assert_into_t, "into() required"); SQLPP_PORTABLE_STATIC_ASSERT(assert_into_arg_is_table, "argument for into() must be a table"); diff --git a/include/sqlpp11/core/clause/select_as.h b/include/sqlpp11/core/clause/select_as.h index c3879f51..068a5fcd 100644 --- a/include/sqlpp11/core/clause/select_as.h +++ b/include/sqlpp11/core/clause/select_as.h @@ -83,7 +83,7 @@ namespace sqlpp template struct provided_tables_of> : public std::conditional>, + sqlpp::detail::type_vector>, sqlpp::detail::type_vector<>> { }; @@ -94,14 +94,9 @@ namespace sqlpp { }; - template - struct provided_optional_tables_of> - : public provided_tables_of> - { - }; - #warning: V1.0 has empty nodes. Is that correct? In either case document the decision here. #warning: Need to add required tables of +#warning: Need to add nodes to allow for parameters to be used. template auto to_sql_string(Context& context, const select_as_t& t) -> std::string diff --git a/include/sqlpp11/core/clause/single_table.h b/include/sqlpp11/core/clause/single_table.h index 58616194..ed61643a 100644 --- a/include/sqlpp11/core/clause/single_table.h +++ b/include/sqlpp11/core/clause/single_table.h @@ -80,6 +80,11 @@ namespace sqlpp using type = detail::type_vector
; }; + template + struct provided_tables_of> : public provided_tables_of
+ { + }; + SQLPP_PORTABLE_STATIC_ASSERT(assert_update_table_arg_is_table_t, "argument for update() must be a table"); template struct check_update_table diff --git a/include/sqlpp11/core/operator/arithmetic_expression.h b/include/sqlpp11/core/operator/arithmetic_expression.h index c0e480f2..cbe4bdad 100644 --- a/include/sqlpp11/core/operator/arithmetic_expression.h +++ b/include/sqlpp11/core/operator/arithmetic_expression.h @@ -92,7 +92,7 @@ namespace sqlpp template using check_arithmetic_args = ::sqlpp::enable_if_t::value and is_numeric::value>; - // L and R are expected to be numeric value types (boolen, integral, unsigned_integral, or floating_point). + // L and R are expected to be numeric value types (boolean, integral, unsigned_integral, or floating_point). template struct arithmetic_value_type { diff --git a/include/sqlpp11/core/query/dynamic.h b/include/sqlpp11/core/query/dynamic.h index b57dd7c7..c735922e 100644 --- a/include/sqlpp11/core/query/dynamic.h +++ b/include/sqlpp11/core/query/dynamic.h @@ -26,6 +26,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -58,7 +59,7 @@ namespace sqlpp template struct nodes_of> { - using type = Expr; + using type = detail::type_vector; }; template diff --git a/include/sqlpp11/core/query/dynamic_fwd.h b/include/sqlpp11/core/query/dynamic_fwd.h new file mode 100644 index 00000000..a5d16bdf --- /dev/null +++ b/include/sqlpp11/core/query/dynamic_fwd.h @@ -0,0 +1,34 @@ +#pragma once + +/* + * Copyright (c) 2024, 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. + */ + +namespace sqlpp +{ + template + struct dynamic_t; + +} // namespace sqlpp diff --git a/include/sqlpp11/core/type_traits/tables_of.h b/include/sqlpp11/core/type_traits/tables_of.h index 1dfc3f49..505ffc52 100644 --- a/include/sqlpp11/core/type_traits/tables_of.h +++ b/include/sqlpp11/core/type_traits/tables_of.h @@ -28,13 +28,13 @@ #include #include +#include namespace sqlpp { - // required_tables_of determines the type_vector of tables referenced by columns within within T. + // required_tables_of recursively determines the type_vector of tables referenced by columns within within T. // column_t or other structs that might reference a table shall specialize this template to indicate their table // requirement. - // Dynamic parts of a query shall wrap their required tables in dynamic_t. template struct required_tables_of { @@ -50,12 +50,23 @@ namespace sqlpp template using required_tables_of_t = typename required_tables_of::type; + // required_static_tables_of recursively determines the type_vector of tables statically referenced by columns within + // within T. column_t or other structs that might reference a table shall specialize this template to indicate their + // table requirement. + // + // Dynamic query parts are ignored. template struct required_static_tables_of { using type = typename required_static_tables_of>::type; }; + template + struct required_static_tables_of> + { + using type = detail::type_vector<>; + }; + template struct required_static_tables_of> { @@ -66,57 +77,49 @@ namespace sqlpp using required_static_tables_of_t = typename required_static_tables_of::type; #warning: need type tests... - //static_assert(required_tables_of_t::size::value == 0, ""); - - // provided_tables_of determines the type_vector of tables provided by the query clause, e.g. by FROM. - // Provided tables can be wrapped in dynamic_t if they are provided through a dynamic join. + // provided_tables_of determines the type_vector of tables provided by a clause, e.g. by FROM. // table_t, cte_ref_t, or other structs that might provide a table in a query need to specialize this template. + // + // Note: In contrast to `required_tables_of` above, this is non-recursive. This is important for instance to prevent + // `SELECT...AS` to leak from `select_column_list`. template struct provided_tables_of { - using type = typename provided_tables_of>::type; + using type = detail::type_vector<>; }; - template - struct provided_tables_of> + template + struct provided_tables_of> : public provided_tables_of { - using type = detail::type_vector_cat_t::type...>; }; template using provided_tables_of_t = typename provided_tables_of::type; + // provided_tables_of determines the type_vector of non-dynamic tables provided by a clause, e.g. by FROM. template - struct provided_static_tables_of + struct provided_static_tables_of : public provided_tables_of { - using type = typename provided_static_tables_of>::type; }; - template - struct provided_static_tables_of> + template + struct provided_static_tables_of> { - using type = detail::type_vector_cat_t::type...>; + using type = detail::type_vector<>; }; template using provided_static_tables_of_t = typename provided_static_tables_of::type; + // provided_tables_of determines the type_vector of outer join tables provided by a clause, e.g. by FROM. template struct provided_optional_tables_of { - using type = typename provided_optional_tables_of>::type; - }; - - template - struct provided_optional_tables_of> - { - using type = detail::type_vector_cat_t::type...>; + using type = detail::type_vector<>; }; template using provided_optional_tables_of_t = typename provided_optional_tables_of::type; - static_assert(provided_tables_of_t::empty(), ""); - } // namespace sqlpp diff --git a/tests/core/types/basic/schema_qualified_table.cpp b/tests/core/types/basic/schema_qualified_table.cpp index 3cdf26d6..11ebdf1f 100644 --- a/tests/core/types/basic/schema_qualified_table.cpp +++ b/tests/core/types/basic/schema_qualified_table.cpp @@ -24,7 +24,6 @@ */ #include "Sample.h" -#include "../compare.h" #include int main(int, char* []) diff --git a/tests/core/types/basic/table_as.cpp b/tests/core/types/basic/table_as.cpp index 0bc916ec..e5d7e842 100644 --- a/tests/core/types/basic/table_as.cpp +++ b/tests/core/types/basic/table_as.cpp @@ -40,7 +40,7 @@ void test_table() static_assert(std::is_same, test::TabBar_::_sqlpp_name_tag>::value, ""); static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); static_assert(std::is_same, sqlpp::provided_tables_of_t>::value, ""); - static_assert(std::is_same, sqlpp::provided_tables_of_t>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); static_assert(std::is_same, sqlpp::required_tables_of_t>::value, ""); diff --git a/tests/core/types/clause/from.cpp b/tests/core/types/clause/from.cpp index 71451873..7a08c7ab 100644 --- a/tests/core/types/clause/from.cpp +++ b/tests/core/types/clause/from.cpp @@ -25,6 +25,7 @@ #include "Sample.h" #include +#include "../../../include/test_helpers.h" void test_from() { @@ -36,10 +37,11 @@ void test_from() // FROM CTE { auto x = cte(sqlpp::alias::x).as(select(foo.id).from(foo).unconditionally()); - auto f = from(x); + auto statement = from(x); using R = decltype(make_table_ref(x)); - using F = decltype(f); + using S = decltype(statement); + using F = extract_clause_t; static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); diff --git a/tests/core/types/clause/into.cpp b/tests/core/types/clause/into.cpp index 1181f1dd..59ddc206 100644 --- a/tests/core/types/clause/into.cpp +++ b/tests/core/types/clause/into.cpp @@ -25,6 +25,7 @@ #include "Sample.h" #include +#include "../../../include/test_helpers.h" SQLPP_CREATE_NAME_TAG(cheese); @@ -45,7 +46,8 @@ void test_into() // Valid into clause { - using I = decltype(sqlpp::into(foo)); + using S = decltype(sqlpp::into(foo)); + using I = extract_clause_t; static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); static_assert(std::is_same, sqlpp::provided_tables_of_t>::value, ""); static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); diff --git a/tests/core/types/clause/single_table.cpp b/tests/core/types/clause/single_table.cpp index 882c8d84..4781760d 100644 --- a/tests/core/types/clause/single_table.cpp +++ b/tests/core/types/clause/single_table.cpp @@ -25,6 +25,7 @@ #include "Sample.h" #include +#include "../../../include/test_helpers.h" SQLPP_CREATE_NAME_TAG(cheese); @@ -45,7 +46,8 @@ void test_single_table() // Valid single table clause { - using S = decltype(sqlpp::single_table(foo)); + using Statement = decltype(sqlpp::single_table(foo)); + using S = extract_clause_t; static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); static_assert(std::is_same, sqlpp::provided_tables_of_t>::value, ""); static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); diff --git a/tests/core/types/type_traits/CMakeLists.txt b/tests/core/types/type_traits/CMakeLists.txt index c40cd99d..cee168b2 100644 --- a/tests/core/types/type_traits/CMakeLists.txt +++ b/tests/core/types/type_traits/CMakeLists.txt @@ -29,4 +29,5 @@ function(test_compile name) endfunction() test_compile(aggregates) +test_compile(tables_of) diff --git a/tests/core/types/type_traits/tables_of.cpp b/tests/core/types/type_traits/tables_of.cpp new file mode 100644 index 00000000..99e3b9af --- /dev/null +++ b/tests/core/types/type_traits/tables_of.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2024, 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 + +SQLPP_CREATE_NAME_TAG(cheese); + +void test_required_tables_of() +{ + // Columns require tables. + { + using T = decltype(test::TabFoo{}.id); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, + ""); + } + + // Tables do not require tables. + { + using T = decltype(test::TabFoo{}); + static_assert(sqlpp::required_tables_of_t::empty(), ""); + static_assert(sqlpp::required_static_tables_of_t::empty(), ""); + } + + // Static expressions require collective tables. + { + using TF = test::TabFoo; + using TB = test::TabBar; + using TC = decltype(test::TabFoo{}.as(cheese)); + using T = decltype(TF{}.id + TB{}.id + TC{}.id); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + } + + // Dynamic expressions require all tables, but on the the static parts contribute to the statically required tables. + { + using TF = test::TabFoo; + using TB = test::TabBar; + using TC = decltype(test::TabFoo{}.as(cheese)); + using T = decltype(TF{}.id < 17 and dynamic(true, TB{}.id < 17) and TC{}.id < 17); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + } +} + +void test_provided_tables_of() +{ + // Columns do not provide tables. + { + using T = decltype(test::TabFoo{}.id); + static_assert(sqlpp::provided_tables_of_t::empty(), ""); + static_assert(sqlpp::provided_static_tables_of_t::empty(), + ""); + static_assert(sqlpp::provided_optional_tables_of_t::empty(), + ""); + } + + // Tables provide tables. + { + using T = test::TabFoo; + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); + } + + // Schema-qualified tables provide tables. + { + using T = decltype(schema_qualified_table({"meme"}, test::TabFoo{}).as(cheese)); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); + } + + // Tables AS provide tables. + { + using T = decltype(test::TabFoo{}.as(cheese)); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); + } + + // SELECT AS provides tables. + { + using T = decltype(select(test::TabFoo{}.id).from(test::TabFoo{}).unconditionally().as(cheese)); + using Ref = sqlpp::select_ref_t; + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); + } + + // JOIN provides tables. + { + using F = test::TabFoo; + using B = test::TabBar; + using T = decltype(F{}.cross_join(B{})); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); + } + + // Dynamic JOIN provides non-static tables. + { + using F = test::TabFoo; + using B = test::TabBar; + using T = decltype(F{}.cross_join(dynamic(true, B{}))); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector<>>::value, ""); + } + + // Left outer join makes right-hand-side table "optional", i.e. columns can by NULL. + { + using F = test::TabFoo; + using B = test::TabBar; + using T = decltype(F{}.left_outer_join(dynamic(true, B{})).on(F{}.id == B{}.id)); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + } + + // Right outer join makes left-hand-side table "optional", i.e. columns can by NULL. + { + using F = test::TabFoo; + using B = test::TabBar; + using T = decltype(F{}.right_outer_join(dynamic(true, B{})).on(F{}.id == B{}.id)); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + } + + // Full outer join makes left-hand-side table "optional", i.e. columns can by NULL. + { + using F = test::TabFoo; + using B = test::TabBar; + using T = decltype(F{}.full_outer_join(dynamic(true, B{})).on(F{}.id == B{}.id)); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + } + + // Nested joins propagate their provided tables. + { + using F = test::TabFoo; + using B = test::TabBar; + using C = decltype(test::TabFoo{}.as(cheese)); + using T = decltype(C{}.cross_join(F{}.full_outer_join(dynamic(true, B{})).on(F{}.id == B{}.id))); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + } + + // Nested joins propagate their provided tables. + { + using F = test::TabFoo; + using B = test::TabBar; + using C = decltype(test::TabFoo{}.as(cheese)); + using T = decltype(F{}.full_outer_join(dynamic(true, B{})).on(F{}.id == B{}.id).cross_join(C{})); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + static_assert(std::is_same, sqlpp::detail::type_vector>::value, ""); + } +} + +int main() +{ + void test_required_tables_of(); + void test_provided_tables_of(); +} + diff --git a/tests/include/test_helpers.h b/tests/include/test_helpers.h index 83a85f2d..5ababdad 100644 --- a/tests/include/test_helpers.h +++ b/tests/include/test_helpers.h @@ -102,3 +102,18 @@ struct is_optional : public std::false_type{}; template struct is_optional<::sqlpp::optional> : public std::true_type{}; +// functions like `from(tab)` yield a statement with a single clause. This extracts the type of that clause. +template +struct extract_clause; + +template +struct extract_clause> +{ + using type = Clause; +}; + +template +using extract_clause_t = typename extract_clause::type; + + +