diff --git a/include/sqlpp11/core/clause/cte.h b/include/sqlpp11/core/clause/cte.h index ba11a437..36752dcf 100644 --- a/include/sqlpp11/core/clause/cte.h +++ b/include/sqlpp11/core/clause/cte.h @@ -237,6 +237,11 @@ namespace sqlpp }; #warning: is table? really? cte_ref needs to be a table, not sure about cte_t + template + struct is_cte> : public std::true_type + { + }; + template struct is_table> : public std::true_type { @@ -259,11 +264,6 @@ namespace sqlpp template struct cte_ref_t { - using _traits = make_traits; - using _nodes = detail::type_vector<>; - using _required_ctes = detail::make_type_set_t; - using _provided_tables = detail::type_set; - template auto as(Statement statement) -> make_cte_t { diff --git a/include/sqlpp11/core/clause/with.h b/include/sqlpp11/core/clause/with.h index a5c03c07..b37e25ee 100644 --- a/include/sqlpp11/core/clause/with.h +++ b/include/sqlpp11/core/clause/with.h @@ -115,8 +115,7 @@ namespace sqlpp auto operator()(Statement statement) -> new_statement_t> { - // FIXME need checks here - // check that no cte refers to any of the ctes to the right +#warning: check that no cte refers to any of the ctes to the right return {statement, _data}; } }; @@ -126,24 +125,23 @@ namespace sqlpp auto to_sql_string(Context& context, const with_data_t& t) -> std::string { using T = with_data_t; - // FIXME: If there is a recursive CTE, add a "RECURSIVE" here - context << " WITH "; - if (T::_is_recursive::value) - { - context << "RECURSIVE "; - } - tuple_to_sql_string(context, t._expressions, tuple_operand{", "}); - context << ' '; - return context; +#warning : If there is a recursive CTE, add a "RECURSIVE" here + return std::string("WITH ") + (T::_is_recursive::value ? "RECURSIVE " : "") + + tuple_to_sql_string(context, t._expressions, tuple_operand{", "}) + " "; + } + + template + auto to_sql_string(Context& context, const blank_with_t& t) -> std::string + { + return to_sql_string(context, t._data); } template auto with(Expressions... cte) -> blank_with_t { - static_assert(logic::all::value...>::value, + static_assert(logic::all::value...>::value, "at least one expression in with is not a common table expression"); - static_assert(logic::none::value...>::value, - "at least one expression in with is an incomplete common table expression"); +#warning: Need to test that cte_t::as yields a cte_ref and that cte_ref is not a cte return {{cte...}}; } } // namespace sqlpp diff --git a/include/sqlpp11/core/type_traits.h b/include/sqlpp11/core/type_traits.h index ce35576a..ce89b41c 100644 --- a/include/sqlpp11/core/type_traits.h +++ b/include/sqlpp11/core/type_traits.h @@ -226,7 +226,6 @@ namespace sqlpp SQLPP_VALUE_TRAIT_GENERATOR(is_prepared_statement) SQLPP_VALUE_TRAIT_GENERATOR(is_union) SQLPP_VALUE_TRAIT_GENERATOR(is_with) - SQLPP_VALUE_TRAIT_GENERATOR(is_cte) SQLPP_VALUE_TRAIT_GENERATOR(is_noop) SQLPP_VALUE_TRAIT_GENERATOR(is_missing) SQLPP_VALUE_TRAIT_GENERATOR(is_pre_join) @@ -592,4 +591,9 @@ namespace sqlpp template struct is_result_clause : public std::false_type {}; + + template + struct is_cte : public std::false_type + { + }; } // namespace sqlpp diff --git a/tests/core/serialize/clause/CMakeLists.txt b/tests/core/serialize/clause/CMakeLists.txt index 5a806a23..ef39a85b 100644 --- a/tests/core/serialize/clause/CMakeLists.txt +++ b/tests/core/serialize/clause/CMakeLists.txt @@ -51,4 +51,5 @@ create_test(union) create_test(update) create_test(update_set) create_test(where) +create_test(with) diff --git a/tests/core/serialize/clause/with.cpp b/tests/core/serialize/clause/with.cpp new file mode 100644 index 00000000..f3b3508e --- /dev/null +++ b/tests/core/serialize/clause/with.cpp @@ -0,0 +1,63 @@ +/* + * 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 "../compare.h" +#include + +int main(int, char* []) +{ + const auto foo = test::TabFoo{}; + const auto bar = test::TabBar{}; + + // No expression (not super useful). + SQLPP_COMPARE(cte(sqlpp::alias::x), "x"); + + // WITH Simple CTE: X AS SELECT + { + const auto x = cte(sqlpp::alias::x).as(select(foo.id).from(foo).unconditionally()); + SQLPP_COMPARE(with(x), "WITH x AS (SELECT tab_foo.id FROM tab_foo) "); + } + + // WITH Non-recursive union CTE: X AS SELECT ... UNION ALL SELECT ... + { + const auto x = + cte(sqlpp::alias::x) + .as(select(foo.id).from(foo).unconditionally().union_all(select(bar.id).from(bar).unconditionally())); + SQLPP_COMPARE(with(x), "WITH x AS (SELECT tab_foo.id FROM tab_foo UNION ALL SELECT tab_bar.id FROM tab_bar) "); + } + + // WITH Recursive union CTE: X AS SELECT ... UNION ALL SELECT ... FROM X ... + { + const auto x_base = cte(sqlpp::alias::x).as(select(sqlpp::value(0).as(sqlpp::alias::a))); + const auto x = x_base.union_all(select((x_base.a + 1).as(sqlpp::alias::a)).from(x_base).where(x_base.a < 10)); + using X = typename std::decay::type; + static_assert(X::_is_recursive, ""); +#warning: Need to test that recursive CTEs are detected as being recursive. + SQLPP_COMPARE(with(x), "WITH RECURSIVE x AS (SELECT 0 AS a UNION ALL SELECT (x.a + 1) AS a FROM x WHERE x.a < 10) "); + } + + return 0; +}