From 6cc2067ef5fd91612f31509384e78d36c74c7657 Mon Sep 17 00:00:00 2001 From: Roland Bock Date: Sun, 19 Nov 2023 12:57:54 +0100 Subject: [PATCH] Allow alias of single-column select to be used as select column (#542) --- include/sqlpp11/select_pseudo_table.h | 28 ++++++++++++++--- include/sqlpp11/table_alias.h | 1 + tests/core/serialize/CMakeLists.txt | 1 + tests/core/serialize/SelectAs.cpp | 43 +++++++++++++++++++++++++++ tests/core/usage/Select.cpp | 9 ++++++ tests/core/usage/SelectType.cpp | 8 ++--- 6 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 tests/core/serialize/SelectAs.cpp diff --git a/include/sqlpp11/select_pseudo_table.h b/include/sqlpp11/select_pseudo_table.h index d3ce771b..39c1c474 100644 --- a/include/sqlpp11/select_pseudo_table.h +++ b/include/sqlpp11/select_pseudo_table.h @@ -50,14 +50,34 @@ namespace sqlpp tag_if>; }; + template + struct select_expression_type { + using value_t = no_value_t; + static constexpr bool _is_expression = false; + static constexpr bool _can_be_null = false; + }; + + template + struct select_expression_type { + using value_t = value_type_of; + static constexpr bool _is_expression = true; + static constexpr bool _can_be_null = can_be_null_t::value; + }; + template struct select_pseudo_table_t : public table_t, select_column_spec_t...> { - using _traits = make_traits::value>>; + using _expr_t = select_expression_type; + using _traits = make_traits< + // Usage as named expression + typename _expr_t::value_t, + tag_if, + tag_if, + // Usage as table + tag::is_table, + tag::is_pseudo_table, + tag_if::value>>; using _nodes = detail::type_vector<>; select_pseudo_table_t(Select select) : _select(select) diff --git a/include/sqlpp11/table_alias.h b/include/sqlpp11/table_alias.h index 5fe6770a..ebc97d6d 100644 --- a/include/sqlpp11/table_alias.h +++ b/include/sqlpp11/table_alias.h @@ -44,6 +44,7 @@ namespace sqlpp using _traits = make_traits, tag::is_table, tag::is_alias, + tag_if::value>, tag_if::value>>; using _nodes = detail::type_vector<>; diff --git a/tests/core/serialize/CMakeLists.txt b/tests/core/serialize/CMakeLists.txt index 74280aa8..51ed9757 100644 --- a/tests/core/serialize/CMakeLists.txt +++ b/tests/core/serialize/CMakeLists.txt @@ -43,6 +43,7 @@ set(test_files Min.cpp Operator.cpp Over.cpp + SelectAs.cpp Some.cpp Sum.cpp TableAlias.cpp diff --git a/tests/core/serialize/SelectAs.cpp b/tests/core/serialize/SelectAs.cpp new file mode 100644 index 00000000..3dae4e53 --- /dev/null +++ b/tests/core/serialize/SelectAs.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023, 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 "compare.h" +#include "Sample.h" +#include + +#include + +SQLPP_ALIAS_PROVIDER(cheese) + +int SelectAs(int, char*[]) +{ + const auto foo = test::TabFoo{}; + const auto bar = test::TabBar{}; + + // SELECT...AS as selectable column + compare(__LINE__, select(foo.omega, select(count(bar.alpha)).from(bar).unconditionally().as(cheese)), "SELECT tab_foo.omega,(SELECT COUNT(tab_bar.alpha) AS count_ FROM tab_bar) AS cheese"); + + return 0; +} diff --git a/tests/core/usage/Select.cpp b/tests/core/usage/Select.cpp index c18cb109..bb01f965 100644 --- a/tests/core/usage/Select.cpp +++ b/tests/core/usage/Select.cpp @@ -61,6 +61,8 @@ void print_row(Row const& row) std::cout << a << ", " << b << std::endl; } +SQLPP_ALIAS_PROVIDER(cheese) + int Select(int, char*[]) { MockDb db = {}; @@ -164,6 +166,7 @@ int Select(int, char*[]) .dynamic_offset(); s.select_flags.add(sqlpp::distinct); s.selected_columns.add(without_table_check(f.omega)); + s.selected_columns.add(select(f.omega).from(f).unconditionally().as(f.delta)); s.from.add(dynamic_cross_join(f)); s.where.add(t.alpha > 7); s.having.add(t.alpha > 7); @@ -210,5 +213,11 @@ int Select(int, char*[]) } } + for (const auto& row : + db(select(f.omega, select(count(t.alpha)).from(t).unconditionally().as(cheese)).from(f).unconditionally())) + { + std::cout << row.omega << " " << row.cheese << std::endl; + } + return 0; } diff --git a/tests/core/usage/SelectType.cpp b/tests/core/usage/SelectType.cpp index e8add31d..90c6f655 100644 --- a/tests/core/usage/SelectType.cpp +++ b/tests/core/usage/SelectType.cpp @@ -260,9 +260,9 @@ int SelectType(int, char*[]) // Test an alias of a select of a single numeric table column { using T = decltype(select(t.alpha).from(t).as(alias::b)); - static_assert(not sqlpp::is_numeric_t::value, "type requirement"); + static_assert(sqlpp::is_numeric_t::value, "type requirement"); static_assert(not sqlpp::is_expression_t::value, "type requirement"); - static_assert(not sqlpp::is_selectable_t::value, "type requirement"); + static_assert(sqlpp::is_selectable_t::value, "type requirement"); static_assert(not sqlpp::require_insert_t::value, "type requirement"); static_assert(not sqlpp::must_not_insert_t::value, "type requirement"); static_assert(not sqlpp::must_not_update_t::value, "type requirement"); @@ -276,9 +276,9 @@ int SelectType(int, char*[]) // Test the column of an alias of a select of an alias of a single numeric table column { using T = decltype(select(t.alpha.as(alias::a)).from(t).as(alias::b)); - static_assert(not sqlpp::is_numeric_t::value, "type requirement"); + static_assert(sqlpp::is_numeric_t::value, "type requirement"); static_assert(not sqlpp::is_expression_t::value, "type requirement"); - static_assert(not sqlpp::is_selectable_t::value, "type requirement"); + static_assert(sqlpp::is_selectable_t::value, "type requirement"); static_assert(not sqlpp::require_insert_t::value, "type requirement"); static_assert(not sqlpp::must_not_insert_t::value, "type requirement"); static_assert(not sqlpp::must_not_update_t::value, "type requirement");