diff --git a/include/sqlpp11/core/clause/cte.h b/include/sqlpp11/core/clause/cte.h index 04083c82..5d89516a 100644 --- a/include/sqlpp11/core/clause/cte.h +++ b/include/sqlpp11/core/clause/cte.h @@ -183,9 +183,6 @@ namespace sqlpp struct cte_t : public cte_member::type..., public enable_join> { -#warning: Need to test this. - constexpr static bool _is_recursive = required_ctes_of_t::template contains>::value; - using _column_tuple_t = std::tuple, FieldSpecs>...>; using _result_row_t = result_row_t; @@ -257,6 +254,13 @@ namespace sqlpp { }; + template + struct is_recursive_cte> : public std::true_type + { +#warning: Need to test this. + constexpr static bool value = required_ctes_of_t::template contains>::value; + }; + template struct is_table> : public std::true_type { diff --git a/include/sqlpp11/core/clause/with.h b/include/sqlpp11/core/clause/with.h index b37e25ee..bd31a904 100644 --- a/include/sqlpp11/core/clause/with.h +++ b/include/sqlpp11/core/clause/with.h @@ -42,8 +42,6 @@ namespace sqlpp template struct with_data_t { - using _is_recursive = logic::any; - with_data_t(Expressions... expressions) : _expressions(expressions...) { } @@ -116,6 +114,7 @@ namespace sqlpp -> new_statement_t> { #warning: check that no cte refers to any of the ctes to the right +#warning: check that ctes have different names return {statement, _data}; } }; @@ -124,9 +123,9 @@ namespace sqlpp template auto to_sql_string(Context& context, const with_data_t& t) -> std::string { - using T = with_data_t; -#warning : If there is a recursive CTE, add a "RECURSIVE" here - return std::string("WITH ") + (T::_is_recursive::value ? "RECURSIVE " : "") + + static constexpr bool _is_recursive = logic::any::value...>::value; + + return std::string("WITH ") + (_is_recursive ? "RECURSIVE " : "") + tuple_to_sql_string(context, t._expressions, tuple_operand{", "}) + " "; } diff --git a/include/sqlpp11/core/type_traits.h b/include/sqlpp11/core/type_traits.h index a3bfa7f1..2a2bbc0a 100644 --- a/include/sqlpp11/core/type_traits.h +++ b/include/sqlpp11/core/type_traits.h @@ -595,4 +595,10 @@ namespace sqlpp struct is_cte : public std::false_type { }; + + template + struct is_recursive_cte : public std::false_type + { + }; + } // namespace sqlpp diff --git a/tests/core/serialize/clause/with.cpp b/tests/core/serialize/clause/with.cpp index 45331e38..699b1826 100644 --- a/tests/core/serialize/clause/with.cpp +++ b/tests/core/serialize/clause/with.cpp @@ -35,13 +35,13 @@ int main(int, char* []) // No expression (not super useful). SQLPP_COMPARE(cte(sqlpp::alias::x), "x"); - // WITH Simple CTE: X AS SELECT + // 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 ... + // WITH non-recursive union CTE: X AS SELECT ... UNION ALL SELECT ... { const auto x = cte(sqlpp::alias::x) @@ -49,7 +49,7 @@ int main(int, char* []) 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 ... + // 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)); @@ -87,5 +87,26 @@ int main(int, char* []) 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) "); } + // WITH two CTEs, no recursive + { + const auto x = cte(sqlpp::alias::x).as(select(foo.id).from(foo).unconditionally()); + const auto y = cte(sqlpp::alias::y).as(select(foo.id).from(foo).unconditionally()); + +#warning: Need to test that CTEs have different names! + SQLPP_COMPARE(with(x, y), "WITH x AS (SELECT tab_foo.id FROM tab_foo), y AS (SELECT tab_foo.id FROM tab_foo) "); + SQLPP_COMPARE(with(y, x), "WITH y AS (SELECT tab_foo.id FROM tab_foo), x AS (SELECT tab_foo.id FROM tab_foo) "); + } + + // WITH two CTEs, one of them recursive + { + 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)); + const auto y = cte(sqlpp::alias::y).as(select(foo.id).from(foo).unconditionally()); + +#warning: Need to test that recursive CTEs are detected as being recursive. + SQLPP_COMPARE(with(x, y), "WITH RECURSIVE x AS (SELECT 0 AS a UNION ALL SELECT (x.a + 1) AS a FROM x WHERE x.a < 10), y AS (SELECT tab_foo.id FROM tab_foo) "); + SQLPP_COMPARE(with(y, x), "WITH RECURSIVE y AS (SELECT tab_foo.id FROM tab_foo), x AS (SELECT 0 AS a UNION ALL SELECT (x.a + 1) AS a FROM x WHERE x.a < 10) "); + } + return 0; }