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

Fix serialize tests for WITH RECURSIVE

This commit is contained in:
Roland Bock 2024-11-01 14:36:44 +01:00
parent 10eaa1f97a
commit aa6ea6c4f0
6 changed files with 185 additions and 21 deletions

View File

@ -40,7 +40,6 @@ namespace sqlpp
public enable_join<table_as_t<TableSpec, NameTag>>
{
using _nodes = detail::type_vector<>;
using _required_ctes = required_ctes_of<TableSpec>;
static_assert(required_tables_of_t<TableSpec>::empty(), "table aliases must not depend on external tables");

View File

@ -41,7 +41,7 @@ namespace sqlpp
struct cte_union_t
{
using _nodes = detail::type_vector<>;
using _required_ctes = detail::make_joined_set_t<required_ctes_of<Lhs>, required_ctes_of<Rhs>>;
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>>;
cte_union_t(Lhs lhs, Rhs rhs) : _lhs(lhs), _rhs(rhs)
@ -58,6 +58,21 @@ namespace sqlpp
Rhs _rhs;
};
template <typename Flag, typename Lhs, typename Rhs>
struct required_ctes_of<cte_union_t<Flag, Lhs, Rhs>>
{
using type = detail::type_vector_cat_t<required_ctes_of_t<Lhs>, required_ctes_of_t<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
template <typename Context, typename Flag, typename Lhs, typename Rhs>
auto to_sql_string(Context& context, const cte_union_t<Flag, Lhs, Rhs>& t) -> std::string
@ -169,7 +184,7 @@ namespace sqlpp
public enable_join<cte_t<NameTagProvider, Statement, FieldSpecs...>>
{
#warning: Need to test this.
constexpr static bool _is_recursive = required_ctes_of<Statement>::template count<cte_ref_t<NameTagProvider>>();
constexpr static bool _is_recursive = required_ctes_of_t<Statement>::template contains<cte_ref_t<NameTagProvider>>::value;
using _column_tuple_t = std::tuple<column_t<cte_ref_t<NameTagProvider>, FieldSpecs>...>;
@ -252,6 +267,17 @@ namespace sqlpp
{
};
template <typename NameTagProvider, typename Statement, typename... ColumnSpecs>
struct provided_ctes_of<cte_t<NameTagProvider, Statement, ColumnSpecs...>>
{
using type = detail::type_vector<cte_ref_t<NameTagProvider>>;
};
template <typename NameTagProvider, typename Statement, typename... ColumnSpecs>
struct provided_static_ctes_of<cte_t<NameTagProvider, Statement, ColumnSpecs...>> : public provided_ctes_of<cte_t<NameTagProvider, Statement, ColumnSpecs...>>
{
};
template <typename Context, typename NameTagProvider, typename Statement, typename... ColumnSpecs>
auto to_sql_string(Context& context, const cte_t<NameTagProvider, Statement, ColumnSpecs...>& t) -> std::string
{
@ -269,7 +295,7 @@ namespace sqlpp
{
static_assert(required_tables_of_t<Statement>::empty(),
"common table expression must not use unknown tables");
static_assert(not required_ctes_of<Statement>::template count<NameTagProvider>(),
static_assert(not required_ctes_of_t<Statement>::template contains<NameTagProvider>::value,
"common table expression must not self-reference in the first part, use union_all/union_distinct "
"for recursion");
@ -295,6 +321,17 @@ namespace sqlpp
{
};
template<typename NameTagProvider>
struct required_ctes_of<cte_ref_t<NameTagProvider>>
{
using type = sqlpp::detail::type_vector<cte_ref_t<NameTagProvider>>;
};
template<typename NameTagProvider>
struct required_static_ctes_of<cte_ref_t<NameTagProvider>> : public required_ctes_of<cte_ref_t<NameTagProvider>>
{
};
template <typename Context, typename NameTagProvider>
auto to_sql_string(Context& context, const cte_ref_t<NameTagProvider>&) -> std::string
{

View File

@ -75,14 +75,16 @@ namespace sqlpp
template <typename Needle, typename Replacement>
using _new_statement_t = typename _policies_update_t<Needle, Replacement>::type;
using _all_required_ctes = detail::make_joined_set_t<required_ctes_of<Policies>...>;
using _all_provided_ctes = detail::make_joined_set_t<provided_ctes_of<Policies>...>;
using _all_required_ctes = detail::type_vector_cat_t<required_ctes_of_t<Policies>...>;
using _all_provided_ctes = detail::type_vector_cat_t<provided_ctes_of_t<Policies>...>;
using _all_required_tables = detail::type_vector_cat_t<required_tables_of_t<Policies>...>;
using _all_provided_tables = detail::type_vector_cat_t<provided_tables_of_t<Policies>...>;
using _all_provided_optional_tables = detail::type_vector_cat_t<provided_optional_tables_of_t<Policies>...>;
#warning: provided_aggregates_of needs to be replaced with type_vector, too
using _all_provided_aggregates = detail::make_joined_set_t<provided_aggregates_of<Policies>...>;
using _required_tables_of = detail::copy_if_t<_all_required_tables, _all_provided_tables::template contains_not>;
using _required_ctes_of = detail::copy_if_t<_all_required_ctes, _all_provided_ctes::template contains_not>;
template <typename Expression>
static constexpr bool _no_unknown_tables = _all_provided_tables::template contains_all<required_tables_of_t<Expression>>::value;
@ -110,12 +112,6 @@ namespace sqlpp
template <template <typename> class Predicate>
using any_t = logic::any<Predicate<Policies>::value...>;
// The tables not covered by the from.
//using _required_tables = detail::make_difference_set_t<_all_required_tables, _all_provided_tables>;
// The common table expressions not covered by the with.
using _required_ctes = detail::make_difference_set_t<_all_required_ctes, _all_provided_ctes>;
using _result_type_provider = detail::get_last_if_t<is_result_clause, noop, Policies...>;
struct _result_methods_t : public _result_type_provider::template _result_methods_t<_statement_t>
@ -127,9 +123,8 @@ namespace sqlpp
// - the select is complete (leaks no table requirements or cte requirements)
static constexpr bool _can_be_used_as_table()
{
#warning: reactivate
return has_result_row<_statement_t>::value and /*_required_tables::size::value == 0 and*/
_required_ctes::size::value == 0;
return has_result_row<_statement_t>::value and _required_tables_of::empty() == 0 and
_required_ctes_of::empty();
}
using _value_type =
@ -149,10 +144,9 @@ namespace sqlpp
// required_tables and _required_ctes are defined above
using _cte_check =
typename std::conditional<_required_ctes::size::value == 0, consistent_t, assert_no_unknown_ctes_t>::type;
#warning: reactivate
using _table_check = consistent_t;
//typename std::conditional<_required_tables::size::value == 0, consistent_t, assert_no_unknown_tables_t>::type;
typename std::conditional<_required_ctes_of::empty() == 0, consistent_t, assert_no_unknown_ctes_t>::type;
using _table_check =
typename std::conditional<_required_tables_of::empty(), consistent_t, assert_no_unknown_tables_t>::type;
using _parameter_check = typename std::
conditional<_parameters::empty(), consistent_t, assert_no_parameters_t>::type;
};

View File

@ -46,6 +46,7 @@
#include <sqlpp11/core/type_traits/nodes_of.h>
#include <sqlpp11/core/type_traits/optional.h>
#include <sqlpp11/core/type_traits/value_type.h>
#include <sqlpp11/core/type_traits/ctes_of.h>
#include <sqlpp11/core/type_traits/tables_of.h>
namespace sqlpp
@ -288,8 +289,6 @@ namespace sqlpp
template <typename T> \
using trait##_of = typename detail::trait##_of_impl<T>::type;
SQLPP_RECURSIVE_TRAIT_SET_GENERATOR(required_ctes)
SQLPP_RECURSIVE_TRAIT_SET_GENERATOR(provided_ctes)
SQLPP_RECURSIVE_TRAIT_SET_GENERATOR(provided_aggregates)
template <typename T>

View File

@ -0,0 +1,107 @@
#pragma once
/*
* Copyright (c) 2024, Roland Bock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sqlpp11/core/type_traits/nodes_of.h>
#include <sqlpp11/core/detail/type_vector.h>
namespace sqlpp
{
// required_ctes_of determines the type_vector of ctes referenced by columns within within T.
// column_t or other structs that might reference a table shall specialize this template to indicate their table
// requirement.
// Dynamic parts of a query shall wrap their required ctes in dynamic_t.
template<typename T>
struct required_ctes_of
{
using type = typename required_ctes_of<nodes_of_t<T>>::type;
};
template<typename... T>
struct required_ctes_of<detail::type_vector<T...>>
{
using type = detail::type_vector_cat_t<typename required_ctes_of<T>::type...>;
};
template<typename T>
using required_ctes_of_t = typename required_ctes_of<T>::type;
template<typename T>
struct required_static_ctes_of
{
using type = typename required_static_ctes_of<nodes_of_t<T>>::type;
};
template<typename... T>
struct required_static_ctes_of<detail::type_vector<T...>>
{
using type = detail::type_vector_cat_t<typename required_static_ctes_of<T>::type...>;
};
template<typename T>
using required_static_ctes_of_t = typename required_static_ctes_of<T>::type;
#warning: need type tests...
//static_assert(required_ctes_of_t<int>::size::value == 0, "");
// provided_ctes_of determines the type_vector of ctes provided by the query clause, e.g. by FROM.
// Provided ctes can be wrapped in dynamic_t if they are provided through a dynamic join.
// table_t, cte_ref_t, or other structs that might provide a table in a query need to specialize this template.
template <typename T>
struct provided_ctes_of
{
using type = typename provided_ctes_of<nodes_of_t<T>>::type;
};
template <typename... T>
struct provided_ctes_of<detail::type_vector<T...>>
{
using type = detail::type_vector_cat_t<typename provided_ctes_of<T>::type...>;
};
template <typename T>
using provided_ctes_of_t = typename provided_ctes_of<T>::type;
template <typename T>
struct provided_static_ctes_of
{
using type = typename provided_static_ctes_of<nodes_of_t<T>>::type;
};
template <typename... T>
struct provided_static_ctes_of<detail::type_vector<T...>>
{
using type = detail::type_vector_cat_t<typename provided_static_ctes_of<T>::type...>;
};
template <typename T>
using provided_static_ctes_of_t = typename provided_static_ctes_of<T>::type;
static_assert(provided_ctes_of_t<int>::empty(), "");
} // namespace sqlpp

View File

@ -53,8 +53,36 @@ int main(int, char* [])
{
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));
#warning: Remove debug code.
/*
using Lhs = sqlpp::statement_t<sqlpp::no_with_t, sqlpp::select_t, sqlpp::no_select_flag_list_t,
sqlpp::select_column_list_t<sqlpp::expression_as<sqlpp::value_t<int>, sqlpp::alias::a_t>>,
sqlpp::no_from_t, sqlpp::no_where_t<true>, sqlpp::no_group_by_t, sqlpp::no_having_t,
sqlpp::no_order_by_t, sqlpp::no_limit_t, sqlpp::no_offset_t, sqlpp::no_union_t,
sqlpp::no_for_update_t>;
using Rhs = sqlpp::statement_t<
sqlpp::no_with_t, sqlpp::select_t, sqlpp::no_select_flag_list_t,
sqlpp::select_column_list_t<sqlpp::expression_as<
sqlpp::arithmetic_expression<
sqlpp::column_t<sqlpp::cte_ref_t<sqlpp::alias::x_t>,
sqlpp::field_spec_t<sqlpp::alias::a_t::_sqlpp_name_tag, sqlpp::integral>>,
sqlpp::plus, int>,
sqlpp::alias::a_t>>,
sqlpp::from_t<sqlpp::cte_ref_t<sqlpp::alias::x_t>>,
sqlpp::where_t<sqlpp::comparison_expression<
sqlpp::column_t<sqlpp::cte_ref_t<sqlpp::alias::x_t>,
sqlpp::field_spec_t<sqlpp::alias::a_t::_sqlpp_name_tag, sqlpp::integral>>,
sqlpp::less, int>>,
sqlpp::no_group_by_t, sqlpp::no_having_t, sqlpp::no_order_by_t, sqlpp::no_limit_t, sqlpp::no_offset_t,
sqlpp::no_union_t, sqlpp::no_for_update_t>;
//sqlpp::required_ctes_of_t<Rhs>::hansi;
using U = sqlpp::cte_union_t<
sqlpp::all_t, Lhs, Rhs>;
using X = typename std::decay<decltype(x)>::type;
//X::hansi;
static_assert(X::_is_recursive, "");
*/
#warning: Need to test that recursive CTEs are detected as being recursive.
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) ");
}