From 3f05ea7c6bf84c82085147edad54b47757ca23c3 Mon Sep 17 00:00:00 2001 From: Roland Bock Date: Sat, 2 Nov 2024 22:10:26 +0100 Subject: [PATCH] Add dependency check for CTEs in with CTEs can use other CTEs iff they have been defined to the left. --- include/sqlpp11/core/clause/cte.h | 22 +++++++++------------- include/sqlpp11/core/clause/with.h | 26 ++++++++++++++++++++++++-- tests/core/serialize/clause/cte.cpp | 14 ++++++++++++++ tests/core/serialize/clause/with.cpp | 9 +++++++++ 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/include/sqlpp11/core/clause/cte.h b/include/sqlpp11/core/clause/cte.h index 5d89516a..07bc94fe 100644 --- a/include/sqlpp11/core/clause/cte.h +++ b/include/sqlpp11/core/clause/cte.h @@ -40,8 +40,6 @@ namespace sqlpp template struct cte_union_t { - using _nodes = detail::type_vector<>; - using _required_ctes = detail::type_vector_cat_t, required_ctes_of_t>; using _parameters = detail::type_vector_cat_t, parameters_of>; cte_union_t(Lhs lhs, Rhs rhs) : _lhs(lhs), _rhs(rhs) @@ -58,19 +56,11 @@ namespace sqlpp Rhs _rhs; }; +#warning: need to test nodes of union! template - struct required_ctes_of> + struct nodes_of> { - using type = detail::type_vector_cat_t, required_ctes_of_t>; - }; - - template - struct required_static_ctes_of> - { - using type = typename std::conditional< - is_dynamic::value, - provided_static_ctes_of_t, - detail::type_vector_cat_t, provided_static_ctes_of_t>>::type; + using type = detail::type_vector; }; // Interpreters @@ -271,6 +261,12 @@ namespace sqlpp { }; + template + struct nodes_of> + { + using type = Statement; + }; + template struct provided_ctes_of> { diff --git a/include/sqlpp11/core/clause/with.h b/include/sqlpp11/core/clause/with.h index bd31a904..2c2a43a3 100644 --- a/include/sqlpp11/core/clause/with.h +++ b/include/sqlpp11/core/clause/with.h @@ -113,8 +113,6 @@ namespace sqlpp auto operator()(Statement statement) -> 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}; } }; @@ -135,11 +133,35 @@ namespace sqlpp return to_sql_string(context, t._data); } + // CTEs can depend on CTEs defined before (in the same query). + // `have_correct_dependencies` checks that by walking the CTEs from left to right and building a type vector that + // contains the CTE it already has looked at. + template + struct have_correct_dependencies_impl; + + template + struct have_correct_dependencies_impl: public std::true_type {}; + + template + struct have_correct_dependencies_impl + { + using allowed_ctes = detail::type_vector_cat_t>; + static constexpr bool value = allowed_ctes::template contains_all>::value and + have_correct_dependencies_impl::value; + }; + + template + struct have_correct_dependencies + { + static constexpr bool value = have_correct_dependencies_impl, CTEs...>::value; + }; template auto with(Expressions... cte) -> blank_with_t { static_assert(logic::all::value...>::value, "at least one expression in with is not a common table expression"); + static_assert(have_correct_dependencies::value, "at least one CTE depends on another CTE that is not defined (yet)"); +#warning: check that ctes have different names #warning: Need to test that cte_t::as yields a cte_ref and that cte_ref is not a cte return {{cte...}}; } diff --git a/tests/core/serialize/clause/cte.cpp b/tests/core/serialize/clause/cte.cpp index 60cc51cc..61e378cf 100644 --- a/tests/core/serialize/clause/cte.cpp +++ b/tests/core/serialize/clause/cte.cpp @@ -77,5 +77,19 @@ int main(int, char* []) SQLPP_COMPARE(all_of(y), "y.a"); } + // A CTE depending on another CTE + { + const auto x = cte(sqlpp::alias::x).as(select(foo.id).from(foo).unconditionally()); + const auto y = cte(sqlpp::alias::y).as(select(x.id, sqlpp::value(7).as(sqlpp::alias::a)).from(x).unconditionally()); + const auto z = y.as(sqlpp::alias::z); + SQLPP_COMPARE(y, "y AS (SELECT x.id, 7 AS a FROM x)"); + SQLPP_COMPARE(make_table_ref(y), "y"); + SQLPP_COMPARE(y.id, "y.id"); + SQLPP_COMPARE(z, "y AS z"); + SQLPP_COMPARE(z.id, "z.id"); + SQLPP_COMPARE(all_of(y), "y.id, y.a"); + SQLPP_COMPARE(all_of(z), "z.id, z.a"); + } + return 0; } diff --git a/tests/core/serialize/clause/with.cpp b/tests/core/serialize/clause/with.cpp index 699b1826..836c93a6 100644 --- a/tests/core/serialize/clause/with.cpp +++ b/tests/core/serialize/clause/with.cpp @@ -108,5 +108,14 @@ int main(int, char* []) 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) "); } + // WITH two CTEs, second depends on first + { + const auto x = cte(sqlpp::alias::x).as(select(foo.id).from(foo).unconditionally()); + const auto y = cte(sqlpp::alias::y).as(select(x.id).from(x).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 x.id FROM x) "); + } + return 0; }