From 5578cce232e21e122f1cb99e49987cdd65ffb713 Mon Sep 17 00:00:00 2001 From: Roland Bock Date: Tue, 23 Jul 2024 20:27:06 +0200 Subject: [PATCH] Rewrite select_as and add type tests --- include/sqlpp11/column.h | 3 +- include/sqlpp11/cte.h | 22 +++- include/sqlpp11/for_update.h | 5 +- include/sqlpp11/from.h | 2 +- include/sqlpp11/join.h | 5 +- include/sqlpp11/parameterized_verbatim.h | 9 ++ include/sqlpp11/pre_join.h | 8 +- include/sqlpp11/schema_qualified_table.h | 5 +- include/sqlpp11/select_as.h | 92 +++++++++++++ include/sqlpp11/select_column_list.h | 39 +++--- include/sqlpp11/select_pseudo_table.h | 100 --------------- include/sqlpp11/single_table.h | 5 +- include/sqlpp11/table.h | 5 +- include/sqlpp11/table_alias.h | 4 +- include/sqlpp11/type_traits.h | 4 +- include/sqlpp11/using.h | 2 +- tests/core/serialize/ForUpdate.cpp | 3 + tests/core/serialize/SelectAs.cpp | 13 ++ tests/core/types/CMakeLists.txt | 1 + tests/core/types/select_as.cpp | 157 +++++++++++++++++++++++ tests/core/usage/Select.cpp | 7 + 21 files changed, 351 insertions(+), 140 deletions(-) create mode 100644 include/sqlpp11/select_as.h delete mode 100644 include/sqlpp11/select_pseudo_table.h create mode 100644 tests/core/types/select_as.cpp diff --git a/include/sqlpp11/column.h b/include/sqlpp11/column.h index 51608433..74fd6fdc 100644 --- a/include/sqlpp11/column.h +++ b/include/sqlpp11/column.h @@ -67,7 +67,8 @@ namespace sqlpp template auto table() const -> _table { - static_assert(is_table_t::value, "cannot call get_table for columns of a sub-selects or cte"); +#warning: subselects use pseudo-columns, cte should do the same, I guess? + static_assert(is_table::value, "cannot call get_table for columns of a sub-select or cte"); return _table{}; } diff --git a/include/sqlpp11/cte.h b/include/sqlpp11/cte.h index 290f3b27..30b18e33 100644 --- a/include/sqlpp11/cte.h +++ b/include/sqlpp11/cte.h @@ -150,7 +150,7 @@ namespace sqlpp template struct cte_t : public cte_base::type... { - using _traits = make_traits; // FIXME: is table? really? + using _traits = make_traits; using _nodes = detail::type_vector<>; using _provided_tables = detail::type_set; using _required_ctes = detail::make_joined_set_t, detail::type_set>; @@ -216,8 +216,16 @@ namespace sqlpp Statement _statement; }; - template - struct name_tag_of> : public name_tag_of{}; +#warning: is table? really? + template + struct is_table> : public std::true_type + { + }; + + template + struct name_tag_of> : public name_tag_of + { + }; template Context& serialize(Context& context, const cte_t& t) @@ -235,7 +243,7 @@ namespace sqlpp template struct cte_ref_t { - using _traits = make_traits; // FIXME: is table? really? + using _traits = make_traits; using _nodes = detail::type_vector<>; using _required_ctes = detail::make_type_set_t; using _provided_tables = detail::type_set; @@ -253,7 +261,11 @@ namespace sqlpp } }; - template +#warning: is table? really? + template + struct is_table> : public std::true_type{}; + + template struct name_tag_of> : public name_tag_of{}; template diff --git a/include/sqlpp11/for_update.h b/include/sqlpp11/for_update.h index 70c89131..226e0e32 100644 --- a/include/sqlpp11/for_update.h +++ b/include/sqlpp11/for_update.h @@ -98,9 +98,8 @@ namespace sqlpp return context; } - template - auto for_update(T&& t) -> decltype(statement_t().for_update(std::forward(t))) + auto for_update() -> decltype(statement_t().for_update()) { - return statement_t().for_update(std::forward(t)); + return statement_t().for_update(); } } // namespace sqlpp diff --git a/include/sqlpp11/from.h b/include/sqlpp11/from.h index d9254723..633247e1 100644 --- a/include/sqlpp11/from.h +++ b/include/sqlpp11/from.h @@ -78,7 +78,7 @@ namespace sqlpp { using type = static_combined_check_t< static_check_t::value, assert_from_not_pre_join_t>, - static_check_t::value, assert_from_table_t>, + static_check_t::value, assert_from_table_t>, static_check_t::size::value == 0, assert_from_dependency_free_t>, static_check_t::size::value == detail::make_name_of_set_t>::size::value, diff --git a/include/sqlpp11/join.h b/include/sqlpp11/join.h index 8d47c33a..5e5299b5 100644 --- a/include/sqlpp11/join.h +++ b/include/sqlpp11/join.h @@ -35,7 +35,7 @@ namespace sqlpp template struct join_t { - using _traits = make_traits; + using _traits = make_traits; using _nodes = detail::type_vector; using _can_be_null = std::false_type; using _provided_tables = provided_tables_of; @@ -81,6 +81,9 @@ namespace sqlpp On _on; }; + template + struct is_table> : public std::true_type{}; + template Context& serialize(Context& context, const join_t& t) { diff --git a/include/sqlpp11/parameterized_verbatim.h b/include/sqlpp11/parameterized_verbatim.h index cbc72732..a0b96739 100644 --- a/include/sqlpp11/parameterized_verbatim.h +++ b/include/sqlpp11/parameterized_verbatim.h @@ -56,6 +56,15 @@ namespace sqlpp std::string _verbatim_lhs, _verbatim_rhs; }; + template + struct value_type_of> + { + using type = ValueType; + }; + + template + struct nodes_of> : public nodes_of{}; + template Context& serialize(Context& context, const parameterized_verbatim_t& t) { diff --git a/include/sqlpp11/pre_join.h b/include/sqlpp11/pre_join.h index 12faaf80..08e83806 100644 --- a/include/sqlpp11/pre_join.h +++ b/include/sqlpp11/pre_join.h @@ -42,8 +42,8 @@ namespace sqlpp struct check_pre_join { using type = static_combined_check_t< - static_check_t::value, assert_pre_join_lhs_table_t>, - static_check_t::value, assert_pre_join_rhs_table_t>, + static_check_t::value, assert_pre_join_lhs_table_t>, + static_check_t::value, assert_pre_join_rhs_table_t>, static_check_t::value, assert_pre_join_rhs_no_join_t>, static_check_t>, detail::make_name_of_set_t>>::value, @@ -93,8 +93,8 @@ namespace sqlpp using _can_be_null = std::false_type; using _provided_outer_tables = typename JoinType::template _provided_outer_tables; - static_assert(is_table_t::value, "lhs argument for join() has to be a table or join"); - static_assert(is_table_t::value, "rhs argument for join() has to be a table"); + static_assert(is_table::value, "lhs argument for join() has to be a table or join"); + static_assert(is_table::value, "rhs argument for join() has to be a table"); static_assert(not is_join_t::value, "rhs argument for join must not be a join"); static_assert(detail::is_disjunct_from, provided_tables_of>::value, diff --git a/include/sqlpp11/schema_qualified_table.h b/include/sqlpp11/schema_qualified_table.h index 5d6b78a1..09d561c6 100644 --- a/include/sqlpp11/schema_qualified_table.h +++ b/include/sqlpp11/schema_qualified_table.h @@ -40,7 +40,7 @@ namespace sqlpp template struct schema_qualified_table_t { - using _traits = make_traits, tag::is_table>; + using _traits = make_traits>; using _nodes = detail::type_vector<>; using _required_ctes = detail::type_set<>; @@ -61,6 +61,9 @@ namespace sqlpp Table _table; }; + template + struct is_table> : public std::true_type {}; + template Context& serialize(Context& context, const schema_qualified_table_t& t) { diff --git a/include/sqlpp11/select_as.h b/include/sqlpp11/select_as.h new file mode 100644 index 00000000..e8482257 --- /dev/null +++ b/include/sqlpp11/select_as.h @@ -0,0 +1,92 @@ +#pragma once + +/* + * Copyright (c) 2013-2016, 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 +#include + +namespace sqlpp +{ + // pseudo_column_t is the column of a sub-select used as a table, for instance. +#warning: Maybe use this for CTE as well +#warning: Should be in its own header + template + struct pseudo_column_t : public enable_as> + { + }; + template + struct name_tag_of> : public name_tag_of + { + }; + + template + struct value_type_of> : public value_type_of + { + }; + + template + struct select_as_t + : public ColumnSpecs::_alias_t::template _member_t>... + { + select_as_t(Select select) : _select(select) + { + } + + select_as_t(const select_as_t& rhs) = default; + select_as_t(select_as_t&& rhs) = default; + select_as_t& operator=(const select_as_t& rhs) = default; + select_as_t& operator=(select_as_t&& rhs) = default; + ~select_as_t() = default; + + Select _select; + }; + + // The Select expression has a value in case it has just one column selected. + template + struct value_type_of> : value_type_of
; - static_assert(is_table_t
::value, "argument has to be a table"); +#warning: can't we do this with a table_t<> specialization + static_assert(is_table
::value, "argument has to be a table"); static_assert(required_tables_of_t
::size::value == 0, "table depends on another table"); using _data_t = single_table_data_t
; @@ -84,7 +85,7 @@ namespace sqlpp template struct check_update_table { - using type = static_combined_check_t::value, assert_update_table_arg_is_table_t>>; + using type = static_combined_check_t::value, assert_update_table_arg_is_table_t>>; }; template using check_update_table_t = typename check_update_table
::type; diff --git a/include/sqlpp11/table.h b/include/sqlpp11/table.h index f2d57a4d..2f0dfe26 100644 --- a/include/sqlpp11/table.h +++ b/include/sqlpp11/table.h @@ -41,7 +41,7 @@ namespace sqlpp template struct table_t : public ColumnSpec::_alias_t::template _member_t>... { - using _traits = make_traits; + using _traits = make_traits; using _nodes = detail::type_vector<>; using _provided_tables = detail::type_set
; @@ -102,6 +102,9 @@ namespace sqlpp template struct name_tag_of>: public name_tag_of
{}; + template + struct is_table>: public std::true_type {}; + template Context& serialize(Context& context, const table_t& /*unused*/) { diff --git a/include/sqlpp11/table_alias.h b/include/sqlpp11/table_alias.h index 8fb88b7b..60ac4e63 100644 --- a/include/sqlpp11/table_alias.h +++ b/include/sqlpp11/table_alias.h @@ -42,7 +42,6 @@ namespace sqlpp struct table_alias_t : public ColumnSpec::_alias_t::template _member_t>... { using _traits = make_traits, - tag::is_table, tag::is_alias, tag_if::value>>; @@ -97,6 +96,9 @@ namespace sqlpp Table _table; }; + template + struct is_table> : public std::true_type{}; + template struct name_tag_of> : public name_tag_of{}; diff --git a/include/sqlpp11/type_traits.h b/include/sqlpp11/type_traits.h index 65953fef..1c588c31 100644 --- a/include/sqlpp11/type_traits.h +++ b/include/sqlpp11/type_traits.h @@ -462,7 +462,6 @@ namespace sqlpp SQLPP_VALUE_TRAIT_GENERATOR(is_noop) SQLPP_VALUE_TRAIT_GENERATOR(is_missing) SQLPP_VALUE_TRAIT_GENERATOR(is_return_value) - SQLPP_VALUE_TRAIT_GENERATOR(is_table) SQLPP_VALUE_TRAIT_GENERATOR(is_raw_table) SQLPP_VALUE_TRAIT_GENERATOR(is_pre_join) SQLPP_VALUE_TRAIT_GENERATOR(is_join) @@ -736,6 +735,9 @@ namespace sqlpp template struct has_name : public std::integral_constant, no_name_t>::value> {}; + template + struct is_table : public std::false_type{}; + template struct make_traits { diff --git a/include/sqlpp11/using.h b/include/sqlpp11/using.h index 39f17135..d731cd38 100644 --- a/include/sqlpp11/using.h +++ b/include/sqlpp11/using.h @@ -79,7 +79,7 @@ namespace sqlpp struct check_using { using type = static_combined_check_t< - static_check_t::value...>::value, assert_using_args_are_tables_t>>; + static_check_t::value...>::value, assert_using_args_are_tables_t>>; }; template using check_using_t = typename check_using::type; diff --git a/tests/core/serialize/ForUpdate.cpp b/tests/core/serialize/ForUpdate.cpp index 0b1b5262..f4b90e71 100644 --- a/tests/core/serialize/ForUpdate.cpp +++ b/tests/core/serialize/ForUpdate.cpp @@ -31,6 +31,9 @@ int ForUpdate(int, char* []) { const auto foo = test::TabFoo{}; + compare(__LINE__, sqlpp::for_update(), + " FOR UPDATE "); + compare(__LINE__, select(foo.doubleN).from(foo).unconditionally().for_update(), "SELECT tab_foo.double_n FROM tab_foo FOR UPDATE "); diff --git a/tests/core/serialize/SelectAs.cpp b/tests/core/serialize/SelectAs.cpp index ad22f2a3..e28ac6c2 100644 --- a/tests/core/serialize/SelectAs.cpp +++ b/tests/core/serialize/SelectAs.cpp @@ -32,12 +32,25 @@ SQLPP_ALIAS_PROVIDER(id_count) SQLPP_ALIAS_PROVIDER(cheese) + using namespace sqlpp; + int SelectAs(int, char*[]) { const auto foo = test::TabFoo{}; const auto bar = test::TabBar{}; // SELECT...AS as selectable column + //using S = table_alias_t >, id_count_t> >, from_t, where_t, no_group_by_t, no_having_t, no_order_by_t, no_limit_t, no_offset_t, no_union_t, no_for_update_t>, as_expression >, id_count_t> >, select_column_spec_t >, id_count_t> >, from_t, where_t, no_group_by_t, no_having_t, no_order_by_t, no_limit_t, no_offset_t, no_union_t, no_for_update_t>, as_expression >, id_count_t> > >; + using S = select_pseudo_table_t >, id_count_t> >, from_t, where_t, no_group_by_t, no_having_t, no_order_by_t, no_limit_t, no_offset_t, no_union_t, no_for_update_t>> ; + static_assert(sqlpp::has_value_type::value, ""); + /* + sqlpp::name_tag_of_t::hansi; + sqlpp::value_type_of_t::hansi; + static_assert(sqlpp::has_value_type::value, ""); + static_assert(sqlpp::has_name::value, ""); + */ +#error: The select itself should not offer an "as" that yields a value. +#error: The id_count should offer the alias that offers the value. compare(__LINE__, select(foo.doubleN, select(count(bar.id).as(id_count)).from(bar).unconditionally().as(cheese)), "SELECT tab_foo.double_n,(SELECT COUNT(tab_bar.id) AS id_count FROM tab_bar) AS cheese"); diff --git a/tests/core/types/CMakeLists.txt b/tests/core/types/CMakeLists.txt index 87d527ad..e242357a 100644 --- a/tests/core/types/CMakeLists.txt +++ b/tests/core/types/CMakeLists.txt @@ -42,5 +42,6 @@ test_compile(dynamic) test_compile(in_expression) test_compile(logical_expression) test_compile(result_row) +test_compile(select_as) test_compile(value) diff --git a/tests/core/types/select_as.cpp b/tests/core/types/select_as.cpp new file mode 100644 index 00000000..8f4e8f28 --- /dev/null +++ b/tests/core/types/select_as.cpp @@ -0,0 +1,157 @@ +/* + * 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 "MockDb.h" +#include "Sample.h" +#include + +namespace +{ + auto db = MockDb{}; + + template + using is_same_type = std::is_same, V>; +} + +SQLPP_ALIAS_PROVIDER(always); +SQLPP_ALIAS_PROVIDER(sometimes); +SQLPP_ALIAS_PROVIDER(column) +SQLPP_ALIAS_PROVIDER(table) +SQLPP_ALIAS_PROVIDER(foo) + +template +void test_select_as(Value v) +{ + auto v_not_null = sqlpp::value(v).as(always); + auto v_maybe_null = sqlpp::value(sqlpp::compat::make_optional(v)).as(sometimes); + + using ValueType = sqlpp::value_type_of_t; + using OptValueType = sqlpp::value_type_of_t>; + + // SINGLE VALUE + + // A select of a single value can be used as a value. + static_assert(is_same_type(), ""); + static_assert(is_same_type(), ""); + + // A select of a single value can be named and used as a named value. + static_assert(sqlpp::has_name::value, ""); + static_assert(sqlpp::has_name::value, ""); + + static_assert(is_same_type(), ""); + static_assert(is_same_type(), ""); + + // A select of a single value can be named and used as a pseudo table + static_assert(sqlpp::is_table::value, ""); + static_assert(sqlpp::is_table::value, ""); + + // The column of a single-value pseudo table can be used as named value + static_assert(sqlpp::has_name::value, ""); + static_assert(sqlpp::has_name::value, ""); + + static_assert(is_same_type(), ""); + static_assert(is_same_type(), ""); + + // The column of a single-value pseudo table can be renamed and used as named value + static_assert(sqlpp::has_name::value, ""); + static_assert(sqlpp::has_name::value, ""); + + static_assert(is_same_type(), ""); + static_assert(is_same_type(), ""); + +#warning: implement + + // MULTIPLE VALUES +#warning: write + + // A select of multiple values can not be used as a value. + static_assert(not sqlpp::has_value_type::value, ""); + + // A select of multiple values can be named and used as a named value. + static_assert(sqlpp::has_name::value, ""); + static_assert(not sqlpp::has_value_type::value, ""); + + // A select of multiple values can be named and used as a pseudo table + static_assert(sqlpp::is_table::value, ""); + + // The column of a multi-value pseudo table can be used as named value + static_assert(sqlpp::has_name::value, ""); + static_assert(sqlpp::has_name::value, ""); + + static_assert(is_same_type(), ""); + static_assert(is_same_type(), ""); + + // The column of a multi-value pseudo table can be renamed and used as named value + static_assert(sqlpp::has_name::value, ""); + static_assert(sqlpp::has_name::value, ""); + + static_assert(is_same_type(), ""); + static_assert(is_same_type(), ""); + + +} + +int main() +{ + // boolean + test_select_as(bool{true}); + + // integral + test_select_as(int8_t{7}); + test_select_as(int16_t{7}); + test_select_as(int32_t{7}); + test_select_as(int64_t{7}); + + // unsigned integral + test_select_as(uint8_t{7}); + test_select_as(uint16_t{7}); + test_select_as(uint32_t{7}); + test_select_as(uint64_t{7}); + + // floating point + test_select_as(float{7.7}); + test_select_as(double{7.7}); + + // text + test_select_as('7'); + test_select_as("seven"); + test_select_as(std::string("seven")); + test_select_as(sqlpp::compat::string_view("seven")); + + // blob + test_select_as(std::vector{}); + + // date + test_select_as(::sqlpp::chrono::day_point{}); + + // timestamp + test_select_as(::sqlpp::chrono::microsecond_point{}); + using minute_point = std::chrono::time_point; + test_select_as(minute_point{}); + + // time_of_day + test_select_as(std::chrono::microseconds{}); +} + diff --git a/tests/core/usage/Select.cpp b/tests/core/usage/Select.cpp index 2d496dc6..8fecd95a 100644 --- a/tests/core/usage/Select.cpp +++ b/tests/core/usage/Select.cpp @@ -187,5 +187,12 @@ int Select(int, char*[]) std::cout << row.doubleN << " " << row.cheese << std::endl; } + // checking #584 + auto abs = db.prepare(select(t.alpha).from(t).where(sqlpp::parameterized_verbatim( + "ABS(field1 -", sqlpp::parameter(t.alpha), ")") <= + sqlpp::parameter(sqlpp::unsigned_integral(), param2))); + abs.params.alpha = 7; + abs.params.param2 = 7; + return 0; }