From 135dceeba3af99efd011e6be195fb5c5dbbf52e2 Mon Sep 17 00:00:00 2001 From: Roland Bock Date: Wed, 21 Aug 2024 07:27:57 +0200 Subject: [PATCH] Add tests and fix serialization of select columns --- .../sqlpp11/core/clause/select_column_list.h | 6 +- include/sqlpp11/core/group_by_column.h | 5 ++ include/sqlpp11/core/operator/as_expression.h | 31 ++----- include/sqlpp11/core/to_sql_string.h | 3 +- include/sqlpp11/core/tuple_to_sql_string.h | 36 ++++++++ tests/core/serialize/clause/CMakeLists.txt | 1 + tests/core/serialize/clause/group_by.cpp | 3 +- .../core/serialize/clause/select_columns.cpp | 85 +++++++++++++++++++ 8 files changed, 145 insertions(+), 25 deletions(-) create mode 100644 tests/core/serialize/clause/select_columns.cpp diff --git a/include/sqlpp11/core/clause/select_column_list.h b/include/sqlpp11/core/clause/select_column_list.h index 4c2d78a8..07df6a5b 100644 --- a/include/sqlpp11/core/clause/select_column_list.h +++ b/include/sqlpp11/core/clause/select_column_list.h @@ -330,7 +330,11 @@ namespace sqlpp template auto to_sql_string(Context& context, const std::tuple& t) -> std::string { - return tuple_to_sql_string(context, t, tuple_operand{", "}); + //dynamic(false, foo.id) -> NULL as id + //dynamic(false, foo.id).as(cheesecake) -> NULL AS cheesecake + //max(something) -> max(something) as _max + //max(something.as(cheesecake) -> max(something) AS cheesecake + return tuple_to_sql_string(context, t, tuple_operand_select_column{", "}); } template diff --git a/include/sqlpp11/core/group_by_column.h b/include/sqlpp11/core/group_by_column.h index 2e78056b..b3aea711 100644 --- a/include/sqlpp11/core/group_by_column.h +++ b/include/sqlpp11/core/group_by_column.h @@ -58,6 +58,11 @@ namespace sqlpp { }; + template + struct requires_parentheses> : public requires_parentheses + { + }; + template struct value_type_of> : public value_type_of { diff --git a/include/sqlpp11/core/operator/as_expression.h b/include/sqlpp11/core/operator/as_expression.h index 29088256..8c8dad83 100644 --- a/include/sqlpp11/core/operator/as_expression.h +++ b/include/sqlpp11/core/operator/as_expression.h @@ -80,28 +80,6 @@ namespace sqlpp return operand_to_sql_string(context, t._expression) + " AS " + name_to_sql_string(context, name_tag_of_t::name); } - template - struct dynamic_t; - - // In case of dynamic expression, we want - // - dynamic(true, value(10)).as(cheese) -> 10 AS cheese - // - dynamic(NULL, value(10)).as(cheese) -> 10 AS cheese - // - dynamic(true, tab.id).as(cheese) -> tab.id AS cheese - // - dynamic(NULL, tab.id).as(cheese) -> NULL AS cheese - // - dynamic(true, tab.id) -> tab.id - // - dynamic(NULL, tab.id) -> NULL AS id - template - auto to_sql_string(Context& context, const as_expression, AliasProvider>& t) -> std::string - { - if (t._expression._condition) - { - return operand_to_sql_string(context, t._expression._expr) + " AS " + - name_to_sql_string(context, name_tag_of_t::name); - } - return to_sql_string(context, ::sqlpp::nullopt) + " AS " + - name_to_sql_string(context, name_tag_of_t::name); - } - template using check_as_args = ::sqlpp::enable_if_t< has_value_type::value and not is_alias_t::value and has_name::value @@ -113,10 +91,19 @@ namespace sqlpp return {std::move(expr)}; } + template + struct dynamic_t; + template > constexpr auto as(dynamic_t expr, const AliasProvider&) -> as_expression, AliasProvider> { return {std::move(expr)}; } + template + constexpr auto as(sqlpp::nullopt_t expr, const AliasProvider&) -> as_expression + { + return {std::move(expr)}; + } + } // namespace sqlpp diff --git a/include/sqlpp11/core/to_sql_string.h b/include/sqlpp11/core/to_sql_string.h index f0601e00..1e403a18 100644 --- a/include/sqlpp11/core/to_sql_string.h +++ b/include/sqlpp11/core/to_sql_string.h @@ -266,7 +266,8 @@ namespace sqlpp template auto name_to_sql_string(Context& , const char* t) -> std::string { -#warning: We used to have a version of SQLPP_ALIAS_PROVIDER that escaped the name +#warning: We used to have a version of SQLPP_ALIAS_PROVIDER that marked names as keywords +#warning: IIUC, the standard SQL way of handling keywords as names is to put them in square brackets, MySQL uses backticks, though return std::string(t); } diff --git a/include/sqlpp11/core/tuple_to_sql_string.h b/include/sqlpp11/core/tuple_to_sql_string.h index be92b91a..4eff6b24 100644 --- a/include/sqlpp11/core/tuple_to_sql_string.h +++ b/include/sqlpp11/core/tuple_to_sql_string.h @@ -70,6 +70,42 @@ namespace sqlpp mutable bool need_prefix = false; }; +#warning: need documentation and a better name! + struct tuple_operand_select_column + { + template + auto operator()(Context& context, const T& t, size_t index) const -> std::string + { + const auto prefix = index ? std::string{separator} : std::string{}; + return prefix + operand_to_sql_string(context, t); + } + + template + auto operator()(Context& context, + const as_expression, NameProvider>& t, + size_t index) const -> std::string + { + if (t._expression._condition) + { + return operator()(context, as(t._expression._expr, NameProvider{}), index); + } + return operator()(context, as(sqlpp::nullopt, NameProvider{}), index); + } + + template + auto operator()(Context& context, const sqlpp::dynamic_t& t, size_t index) const -> std::string + { + if (t._condition) + { + return operator()(context, t._expr, index); + } + static_assert(has_name::value, "select columns have to have a name"); + return operator()(context, as(sqlpp::nullopt, t._expr), index); + } + + sqlpp::string_view separator; + }; + struct tuple_clause { template diff --git a/tests/core/serialize/clause/CMakeLists.txt b/tests/core/serialize/clause/CMakeLists.txt index 9f288087..91eb57ad 100644 --- a/tests/core/serialize/clause/CMakeLists.txt +++ b/tests/core/serialize/clause/CMakeLists.txt @@ -30,4 +30,5 @@ function(create_test name) endfunction() create_test(group_by) +create_test(select_columns) diff --git a/tests/core/serialize/clause/group_by.cpp b/tests/core/serialize/clause/group_by.cpp index 6dd850b0..f81612e4 100644 --- a/tests/core/serialize/clause/group_by.cpp +++ b/tests/core/serialize/clause/group_by.cpp @@ -38,7 +38,8 @@ int main(int, char* []) // Plain columns. SQLPP_COMPARE(group_by(foo.id), " GROUP BY tab_foo.id"); - SQLPP_COMPARE(group_by(foo.id, foo.textNnD, foo.boolN), " GROUP BY tab_foo.id, tab_foo.text_nn_d, tab_foo.bool_n"); + SQLPP_COMPARE(group_by(foo.textNnD), " GROUP BY tab_foo.text_nn_d"); + SQLPP_COMPARE(group_by(foo.boolN), " GROUP BY tab_foo.bool_n"); // Multiple plain columns. SQLPP_COMPARE(group_by(foo.id, foo.textNnD, foo.boolN), " GROUP BY tab_foo.id, tab_foo.text_nn_d, tab_foo.bool_n"); diff --git a/tests/core/serialize/clause/select_columns.cpp b/tests/core/serialize/clause/select_columns.cpp new file mode 100644 index 00000000..e65c3e3b --- /dev/null +++ b/tests/core/serialize/clause/select_columns.cpp @@ -0,0 +1,85 @@ +/* + * 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 "Sample.h" +#include "../compare.h" +#include + +SQLPP_ALIAS_PROVIDER(cheese); +SQLPP_ALIAS_PROVIDER(cake); + +int main(int, char* []) +{ + const auto val = sqlpp::value(17); + const auto expr = sqlpp::value(17) + 4; + + const auto foo = test::TabFoo{}; + + // Plain columns. + SQLPP_COMPARE(select_columns(foo.id), "tab_foo.id"); + SQLPP_COMPARE(select_columns(foo.textNnD), "tab_foo.text_nn_d"); + SQLPP_COMPARE(select_columns(foo.boolN), "tab_foo.bool_n"); + + // Multiple plain columns. + SQLPP_COMPARE(select_columns(foo.id, foo.textNnD, foo.boolN), "tab_foo.id, tab_foo.text_nn_d, tab_foo.bool_n"); + + // Single expression + SQLPP_COMPARE(select_columns((foo.id + 17).as(cake)), "(tab_foo.id + 17) AS cake"); + + // Single dynamic column. + SQLPP_COMPARE(select_columns(dynamic(true, foo.id)), "tab_foo.id"); +#warning: This needs a special special to-string strategy in select_columns + SQLPP_COMPARE(select_columns(dynamic(false, foo.id)), "NULL AS id"); + + // Multiple dynamic columns (this is odd if all are dynamic) + SQLPP_COMPARE(select_columns(dynamic(true, foo.id), foo.textNnD, foo.boolN), "tab_foo.id, tab_foo.text_nn_d, tab_foo.bool_n"); + SQLPP_COMPARE(select_columns(foo.id, dynamic(true, foo.textNnD), foo.boolN), "tab_foo.id, tab_foo.text_nn_d, tab_foo.bool_n"); + SQLPP_COMPARE(select_columns(foo.id, foo.textNnD, dynamic(true, foo.boolN)), "tab_foo.id, tab_foo.text_nn_d, tab_foo.bool_n"); + + SQLPP_COMPARE(select_columns(dynamic(false, foo.id), foo.textNnD, foo.boolN), "NULL AS id, tab_foo.text_nn_d, tab_foo.bool_n"); + SQLPP_COMPARE(select_columns(foo.id, dynamic(false, foo.textNnD), foo.boolN), "tab_foo.id, NULL AS text_nn_d, tab_foo.bool_n"); + SQLPP_COMPARE(select_columns(foo.id, foo.textNnD, dynamic(false, foo.boolN)), "tab_foo.id, tab_foo.text_nn_d, NULL AS bool_n"); + + SQLPP_COMPARE(select_columns(foo.id, dynamic(false, foo.textNnD), dynamic(false, foo.boolN)), "tab_foo.id, NULL AS text_nn_d, NULL AS bool_n"); + SQLPP_COMPARE(select_columns(dynamic(false, foo.id), foo.textNnD, dynamic(false, foo.boolN)), "NULL AS id, tab_foo.text_nn_d, NULL AS bool_n"); + SQLPP_COMPARE(select_columns(dynamic(false, foo.id), dynamic(false, foo.textNnD), foo.boolN), "NULL AS id, NULL AS text_nn_d, tab_foo.bool_n"); + + // Single declared column + SQLPP_COMPARE(select_columns(declare_group_by_column(val).as(cheese)), "17 AS cheese"); + SQLPP_COMPARE(select_columns(declare_group_by_column(foo.id + 17).as(cake)), "(tab_foo.id + 17) AS cake"); + + // Mixed declared column + SQLPP_COMPARE(select_columns(foo.id, declare_group_by_column(val).as(cheese)), "tab_foo.id, 17 AS cheese"); + SQLPP_COMPARE(select_columns(declare_group_by_column(val).as(cake), foo.id), "17 AS cake, tab_foo.id"); + + // Mixed dynamic declared column + SQLPP_COMPARE(select_columns(foo.id, dynamic(true, declare_group_by_column(val)).as(cheese)), "tab_foo.id, 17 AS cheese"); + SQLPP_COMPARE(select_columns(dynamic(true, declare_group_by_column(val)).as(cake), foo.id), "17 AS cake, tab_foo.id"); + + SQLPP_COMPARE(select_columns(foo.id, dynamic(false, declare_group_by_column(val)).as(cheese)), "tab_foo.id, NULL AS cheese"); + SQLPP_COMPARE(select_columns(dynamic(false, declare_group_by_column(val)).as(cake), foo.id), "NULL AS cake, tab_foo.id"); + + return 0; +}