0
0
mirror of https://github.com/rbock/sqlpp11.git synced 2024-11-16 04:47:18 +08:00

Add dependency check for CTEs in with

CTEs can use other CTEs iff they have been defined to the left.
This commit is contained in:
Roland Bock 2024-11-02 22:10:26 +01:00
parent f56f20cfc1
commit 3f05ea7c6b
4 changed files with 56 additions and 15 deletions

View File

@ -40,8 +40,6 @@ namespace sqlpp
template <typename Flag, typename Lhs, typename Rhs> template <typename Flag, typename Lhs, typename Rhs>
struct cte_union_t struct cte_union_t
{ {
using _nodes = detail::type_vector<>;
using _required_ctes = detail::type_vector_cat_t<required_ctes_of_t<Lhs>, required_ctes_of_t<Rhs>>;
using _parameters = detail::type_vector_cat_t<parameters_of<Lhs>, parameters_of<Rhs>>; using _parameters = detail::type_vector_cat_t<parameters_of<Lhs>, parameters_of<Rhs>>;
cte_union_t(Lhs lhs, Rhs rhs) : _lhs(lhs), _rhs(rhs) cte_union_t(Lhs lhs, Rhs rhs) : _lhs(lhs), _rhs(rhs)
@ -58,19 +56,11 @@ namespace sqlpp
Rhs _rhs; Rhs _rhs;
}; };
#warning: need to test nodes of union!
template <typename Flag, typename Lhs, typename Rhs> template <typename Flag, typename Lhs, typename Rhs>
struct required_ctes_of<cte_union_t<Flag, Lhs, Rhs>> struct nodes_of<cte_union_t<Flag, Lhs, Rhs>>
{ {
using type = detail::type_vector_cat_t<required_ctes_of_t<Lhs>, required_ctes_of_t<Rhs>>; using type = detail::type_vector<Lhs, Rhs>;
};
template <typename Flag, typename Lhs, typename Rhs>
struct required_static_ctes_of<cte_union_t<Flag, Lhs, Rhs>>
{
using type = typename std::conditional<
is_dynamic<Rhs>::value,
provided_static_ctes_of_t<Lhs>,
detail::type_vector_cat_t<provided_static_ctes_of_t<Lhs>, provided_static_ctes_of_t<Rhs>>>::type;
}; };
// Interpreters // Interpreters
@ -271,6 +261,12 @@ namespace sqlpp
{ {
}; };
template <typename NameTagProvider, typename Statement, typename... ColumnSpecs>
struct nodes_of<cte_t<NameTagProvider, Statement, ColumnSpecs...>>
{
using type = Statement;
};
template <typename NameTagProvider, typename Statement, typename... ColumnSpecs> template <typename NameTagProvider, typename Statement, typename... ColumnSpecs>
struct provided_ctes_of<cte_t<NameTagProvider, Statement, ColumnSpecs...>> struct provided_ctes_of<cte_t<NameTagProvider, Statement, ColumnSpecs...>>
{ {

View File

@ -113,8 +113,6 @@ namespace sqlpp
auto operator()(Statement statement) auto operator()(Statement statement)
-> new_statement_t<consistent_t, typename Statement::_policies_t, no_with_t, with_t<Expressions...>> -> new_statement_t<consistent_t, typename Statement::_policies_t, no_with_t, with_t<Expressions...>>
{ {
#warning: check that no cte refers to any of the ctes to the right
#warning: check that ctes have different names
return {statement, _data}; return {statement, _data};
} }
}; };
@ -135,11 +133,35 @@ namespace sqlpp
return to_sql_string(context, t._data); 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 <typename AllowedCTEs, typename... CTEs>
struct have_correct_dependencies_impl;
template <typename AllowedCTEs>
struct have_correct_dependencies_impl<AllowedCTEs>: public std::true_type {};
template <typename AllowedCTEs, typename CTE, typename... Rest>
struct have_correct_dependencies_impl<AllowedCTEs, CTE, Rest...>
{
using allowed_ctes = detail::type_vector_cat_t<AllowedCTEs, provided_ctes_of_t<CTE>>;
static constexpr bool value = allowed_ctes::template contains_all<required_ctes_of_t<CTE>>::value and
have_correct_dependencies_impl<allowed_ctes, Rest...>::value;
};
template <typename... CTEs>
struct have_correct_dependencies
{
static constexpr bool value = have_correct_dependencies_impl<detail::type_vector<>, CTEs...>::value;
};
template <typename... Expressions> template <typename... Expressions>
auto with(Expressions... cte) -> blank_with_t<Expressions...> auto with(Expressions... cte) -> blank_with_t<Expressions...>
{ {
static_assert(logic::all<is_cte<Expressions>::value...>::value, static_assert(logic::all<is_cte<Expressions>::value...>::value,
"at least one expression in with is not a common table expression"); "at least one expression in with is not a common table expression");
static_assert(have_correct_dependencies<Expressions...>::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 #warning: Need to test that cte_t::as yields a cte_ref and that cte_ref is not a cte
return {{cte...}}; return {{cte...}};
} }

View File

@ -77,5 +77,19 @@ int main(int, char* [])
SQLPP_COMPARE(all_of(y), "y.a"); 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; return 0;
} }

View File

@ -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) "); 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; return 0;
} }