From 7cdcf0f17290654fb49237c3224fde52a888fe82 Mon Sep 17 00:00:00 2001 From: Roland Bock Date: Mon, 4 Nov 2024 07:21:29 +0100 Subject: [PATCH] Add support for dynamic union in CTEs --- include/sqlpp11/core/clause/cte.h | 49 +++++++++++++++++++++-------- tests/core/serialize/clause/cte.cpp | 24 +++++++++++++- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/include/sqlpp11/core/clause/cte.h b/include/sqlpp11/core/clause/cte.h index 7c5cd6c5..607e4d96 100644 --- a/include/sqlpp11/core/clause/cte.h +++ b/include/sqlpp11/core/clause/cte.h @@ -68,6 +68,15 @@ namespace sqlpp return to_sql_string(context, t._lhs) + " UNION " + to_sql_string(context, Flag{}) + to_sql_string(context, t._rhs); } + template + auto to_sql_string(Context& context, const cte_union_t>& t) -> std::string + { + if (t._rhs._condition){ + return to_sql_string(context, t._lhs) + " UNION " + to_sql_string(context, Flag{}) + to_sql_string(context, t._rhs._expr); + } + return to_sql_string(context, t._lhs); + } + template struct cte_t; @@ -116,14 +125,14 @@ namespace sqlpp using union_cte_impl_t = typename union_cte_impl::type; SQLPP_PORTABLE_STATIC_ASSERT(assert_cte_union_args_are_statements_t, "argument for union() must be a statement"); - template + template struct check_cte_union { using type = static_combined_check_t< - static_check_t::value...>::value, assert_cte_union_args_are_statements_t>>; + static_check_t::value, assert_cte_union_args_are_statements_t>>; }; - template - using check_cte_union_t = typename check_cte_union::type; + template + using check_cte_union_t = typename check_cte_union>::type; // cte_member is a helper to add column data members to `cte_t`. template @@ -194,14 +203,15 @@ namespace sqlpp -> union_cte_impl_t, cte_t, FieldSpecs...>> { - static_assert(is_statement_t::value, "argument of union call has to be a statement"); - static_assert(has_policy_t::value, "argument of union call has to be a select"); - static_assert(has_result_row::value, "argument of a clause/union.has to be a (complete) select statement"); + using _rhs = remove_dynamic_t; + static_assert(is_statement_t<_rhs>::value, "argument of union call has to be a statement"); + static_assert(has_policy_t<_rhs, is_select_t>::value, "argument of union call has to be a select"); + static_assert(has_result_row<_rhs>::value, "argument of a clause/union.has to be a (complete) select statement"); - static_assert(std::is_same<_result_row_t, get_result_row_t>::value, + static_assert(std::is_same<_result_row_t, get_result_row_t<_rhs>>::value, "both select statements in a clause/union.have to have the same result columns (type and name)"); - return _union_impl(check_cte_union_t{}, rhs); + return _union_impl(check_cte_union_t{}, rhs); } template @@ -209,11 +219,12 @@ namespace sqlpp -> union_cte_impl_t, cte_t, FieldSpecs...>> { - static_assert(is_statement_t::value, "argument of union call has to be a statement"); - static_assert(has_policy_t::value, "argument of union call has to be a select"); - static_assert(has_result_row::value, "argument of a clause/union.has to be a (complete) select statement"); + using _rhs = remove_dynamic_t; + static_assert(is_statement_t<_rhs>::value, "argument of union call has to be a statement"); + static_assert(has_policy_t<_rhs, is_select_t>::value, "argument of union call has to be a select"); + static_assert(has_result_row<_rhs>::value, "argument of a clause/union.has to be a (complete) select statement"); - static_assert(std::is_same<_result_row_t, get_result_row_t>::value, + static_assert(std::is_same<_result_row_t, get_result_row_t<_rhs>>::value, "both select statements in a clause/union.have to have the same result columns (type and name)"); return _union_impl(check_cte_union_t{}, rhs); @@ -272,6 +283,18 @@ namespace sqlpp using type = Statement; }; + template + struct provided_tables_of> + { + 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> { diff --git a/tests/core/serialize/clause/cte.cpp b/tests/core/serialize/clause/cte.cpp index 61e378cf..5d77e167 100644 --- a/tests/core/serialize/clause/cte.cpp +++ b/tests/core/serialize/clause/cte.cpp @@ -63,7 +63,7 @@ int main(int, char* []) SQLPP_COMPARE(all_of(a), "a.id"); } - // Recursive union CTE: X AS SELECT ... UNION ALL SELECT ... FROM X ... + // Recursive 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)); @@ -91,5 +91,27 @@ int main(int, char* []) SQLPP_COMPARE(all_of(z), "z.id, z.a"); } + // Dynamically recursive 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))); + auto x = x_base.union_all(dynamic(true, select((x_base.a + 1).as(sqlpp::alias::a)).from(x_base).where(x_base.a < 10))); + + SQLPP_COMPARE(x, "x AS (SELECT 0 AS a UNION ALL SELECT (x.a + 1) AS a FROM x WHERE x.a < 10)"); + + x = x_base.union_all(dynamic(false, select((x_base.a + 1).as(sqlpp::alias::a)).from(x_base).where(x_base.a < 10))); + SQLPP_COMPARE(x, "x AS (SELECT 0 AS a)"); + } + + // Dynamically recursive CTE: X AS SELECT ... UNION DISTINCT SELECT ... FROM X ... + { + const auto x_base = cte(sqlpp::alias::x).as(select(sqlpp::value(0).as(sqlpp::alias::a))); + auto x = x_base.union_distinct(dynamic(true, select((x_base.a + 1).as(sqlpp::alias::a)).from(x_base).where(x_base.a < 10))); + + SQLPP_COMPARE(x, "x AS (SELECT 0 AS a UNION DISTINCT SELECT (x.a + 1) AS a FROM x WHERE x.a < 10)"); + + x = x_base.union_distinct(dynamic(false, select((x_base.a + 1).as(sqlpp::alias::a)).from(x_base).where(x_base.a < 10))); + SQLPP_COMPARE(x, "x AS (SELECT 0 AS a)"); + } + return 0; }