From 980daac5178ac712a49aa106f3d20db444fa608e Mon Sep 17 00:00:00 2001 From: Roland Bock Date: Fri, 25 Oct 2024 07:31:56 +0200 Subject: [PATCH] Implement CTE alias (allowing self-join) --- include/sqlpp11/core/clause/cte.h | 60 +++++++++++++++++++++++----- tests/core/serialize/clause/from.cpp | 9 +++++ 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/include/sqlpp11/core/clause/cte.h b/include/sqlpp11/core/clause/cte.h index 01ec9733..dd75ea7f 100644 --- a/include/sqlpp11/core/clause/cte.h +++ b/include/sqlpp11/core/clause/cte.h @@ -134,6 +134,40 @@ namespace sqlpp using type = member_t, FieldSpec>>; }; +#warning: Need to document that you need to be a bit careful with aliased CTEs as we use cte_ref in columns, from, and join. + template + struct cte_as_t : public cte_member::type..., + public enable_join> + { + }; + + template + struct is_table> : public std::true_type + { + }; + + template + struct name_tag_of> : public name_tag_of + { + }; + + template + struct provided_tables_of> + { + using type = sqlpp::detail::type_vector>; + }; + + template + struct provided_static_tables_of> : public provided_tables_of> + { + }; + + template + auto to_sql_string(Context& context, const cte_as_t&) -> std::string + { + return name_to_sql_string(context, name_tag_of_t::name) + " AS " + name_to_sql_string(context, name_tag_of_t::name); + } + template struct cte_t : public cte_member::type..., public enable_join> @@ -151,6 +185,13 @@ namespace sqlpp using _result_row_t = result_row_t; + template + constexpr auto as(const NewNameTagProvider& /*unused*/) const + -> cte_as_t + { + return {}; + } + template auto union_distinct(Rhs rhs) const -> union_cte_impl_t, @@ -205,7 +246,7 @@ namespace sqlpp Statement _statement; }; -#warning: is table? really? +#warning: is table? really? cte_ref needs to be a table, not sure about cte_t template struct is_table> : public std::true_type { @@ -219,13 +260,12 @@ namespace sqlpp template auto to_sql_string(Context& context, const cte_t& t) -> std::string { - return name_to_sql_string(context, name_tag_of_t::name) + " AS (" + - to_sql_string(context, t._statement) + ")"; + return name_to_sql_string(context, name_tag_of_t::name) + " AS (" + + to_sql_string(context, t._statement) + ")"; } - // The cte_t is displayed as NameTagProviderName except within the with: - // - the with needs the - // NameTagProviderName AS (ColumnNames) (select/union) + // The cte_ref_t represents the cte as table in FROM. + // The cte_t needs to be provided by WITH. template struct cte_ref_t { @@ -247,10 +287,13 @@ namespace sqlpp } }; -#warning: is table? really? template struct is_table> : public std::true_type{}; + template + struct name_tag_of> : public name_tag_of + {}; + template struct provided_tables_of> { @@ -262,9 +305,6 @@ namespace sqlpp { }; - template - struct name_tag_of> : public name_tag_of{}; - template auto to_sql_string(Context& context, const cte_ref_t&) -> std::string { diff --git a/tests/core/serialize/clause/from.cpp b/tests/core/serialize/clause/from.cpp index f39eb871..fe70d9db 100644 --- a/tests/core/serialize/clause/from.cpp +++ b/tests/core/serialize/clause/from.cpp @@ -36,6 +36,9 @@ int main() const auto cFoo = foo.as(sqlpp::alias::c); const auto x = cte(sqlpp::alias::x).as(select(foo.id).from(foo).unconditionally()); + const auto xa = x.as(sqlpp::alias::a); + const auto xb = x.as(sqlpp::alias::b); + const auto y = cte(sqlpp::alias::y).as(select(foo.id).from(foo).unconditionally()); // Single table SQLPP_COMPARE(from(foo), " FROM tab_foo"); @@ -53,7 +56,13 @@ int main() // CTE SQLPP_COMPARE(from(x), " FROM x"); + SQLPP_COMPARE(from(foo.join(x).on(x.id == foo.id)), " FROM tab_foo INNER JOIN x ON x.id = tab_foo.id"); SQLPP_COMPARE(from(x.join(foo).on(x.id == foo.id)), " FROM x INNER JOIN tab_foo ON x.id = tab_foo.id"); + SQLPP_COMPARE(from(x.join(y).on(x.id == y.id)), " FROM x INNER JOIN y ON x.id = y.id"); +#warning: Some of these should go into CTE tests + SQLPP_COMPARE(xa, "x AS a"); + SQLPP_COMPARE(xa.id == xb.id, "a.id = b.id"); + SQLPP_COMPARE(from(xa.join(xb).on(xa.id == xb.id)), " FROM x AS a INNER JOIN x AS b ON a.id = b.id"); #warning add tests for dynamic joins