mirror of
https://github.com/rbock/sqlpp11.git
synced 2024-11-16 12:51:13 +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:
parent
f56f20cfc1
commit
3f05ea7c6b
@ -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...>>
|
||||||
{
|
{
|
||||||
|
@ -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...}};
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user