diff --git a/include/sqlpp11/core/basic/value.h b/include/sqlpp11/core/basic/value.h index 8fa034cc..063845c8 100644 --- a/include/sqlpp11/core/basic/value.h +++ b/include/sqlpp11/core/basic/value.h @@ -53,9 +53,6 @@ namespace sqlpp using type = value_type_of_t; }; - template - using check_value_arg = ::sqlpp::enable_if_t, no_value_t>::value and values_are_comparable::value>; - template auto to_sql_string(Context& context, const value_t& t) -> std::string { @@ -63,6 +60,9 @@ namespace sqlpp return to_sql_string(context, t._value); } + template + using check_value_arg = ::sqlpp::enable_if_t, no_value_t>::value and values_are_comparable::value>; + template > auto value(T t) -> value_t { diff --git a/include/sqlpp11/core/clause/group_by.h b/include/sqlpp11/core/clause/group_by.h index 691b2b26..6cabbf84 100644 --- a/include/sqlpp11/core/clause/group_by.h +++ b/include/sqlpp11/core/clause/group_by.h @@ -93,10 +93,10 @@ namespace sqlpp struct check_group_by { using type = static_combined_check_t< - static_check_t::value...>::value, assert_group_by_args_are_columns_t>>; + static_check_t>::value...>::value, assert_group_by_args_are_columns_t>>; }; template - using check_group_by_t = typename check_group_by::type; + using check_group_by_t = typename check_group_by...>::type; // NO GROUP BY YET struct no_group_by_t @@ -125,7 +125,7 @@ namespace sqlpp template auto group_by(Columns... columns) const - -> _new_statement_t...>, group_by_t> + -> _new_statement_t, group_by_t> { static_assert(sizeof...(Columns), "at least one column required in group_by()"); @@ -153,7 +153,7 @@ namespace sqlpp template auto to_sql_string(Context& context, const group_by_data_t& t) -> std::string { - return " GROUP BY " + tuple_to_sql_string(context, t._columns, tuple_operand{", "}); + return " GROUP BY " + tuple_to_sql_string(context, t._columns, tuple_operand_no_dynamic{", "}); } template diff --git a/include/sqlpp11/core/group_by_column.h b/include/sqlpp11/core/group_by_column.h index b052d11e..2e78056b 100644 --- a/include/sqlpp11/core/group_by_column.h +++ b/include/sqlpp11/core/group_by_column.h @@ -63,6 +63,13 @@ namespace sqlpp { }; + template + auto to_sql_string(Context& context, const group_by_column& t) -> std::string + { +#warning: Untested + return to_sql_string(context, t._expr); + } + template using check_declare_group_by_column_args = ::sqlpp::enable_if_t::value and not is_group_by_column::value>; diff --git a/include/sqlpp11/core/operator/as_expression.h b/include/sqlpp11/core/operator/as_expression.h index 0bb2212e..29088256 100644 --- a/include/sqlpp11/core/operator/as_expression.h +++ b/include/sqlpp11/core/operator/as_expression.h @@ -80,6 +80,28 @@ 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 @@ -91,8 +113,6 @@ namespace sqlpp return {std::move(expr)}; } - template - struct dynamic_t; template > constexpr auto as(dynamic_t expr, const AliasProvider&) -> as_expression, AliasProvider> { diff --git a/include/sqlpp11/core/query/dynamic.h b/include/sqlpp11/core/query/dynamic.h index 417999fa..e56ad957 100644 --- a/include/sqlpp11/core/query/dynamic.h +++ b/include/sqlpp11/core/query/dynamic.h @@ -52,7 +52,7 @@ namespace sqlpp Expr _expr; }; - // No value_type_of or name_tag_of defined for dynamic_t, to prevent its usage outside of select columns. + // No value_type_of or name_tag_of defined for dynamic_t, to prevent its usage outside of select columns or similar explicitly allowed areas. template struct nodes_of> diff --git a/include/sqlpp11/core/tuple_to_sql_string.h b/include/sqlpp11/core/tuple_to_sql_string.h index 41e3902a..be92b91a 100644 --- a/include/sqlpp11/core/tuple_to_sql_string.h +++ b/include/sqlpp11/core/tuple_to_sql_string.h @@ -67,7 +67,7 @@ namespace sqlpp } sqlpp::string_view separator; - bool need_prefix = false; + mutable bool need_prefix = false; }; struct tuple_clause diff --git a/tests/core/serialize/CMakeLists.txt b/tests/core/serialize/CMakeLists.txt index dec91467..aee6be6e 100644 --- a/tests/core/serialize/CMakeLists.txt +++ b/tests/core/serialize/CMakeLists.txt @@ -54,4 +54,6 @@ foreach(test_file IN LISTS test_files) endforeach() add_subdirectory(aggregate_function) +add_subdirectory(clause) add_subdirectory(operator) +add_subdirectory(query) diff --git a/tests/core/serialize/SelectColumns.cpp b/tests/core/serialize/SelectColumns.cpp index e9d5824f..6f0bd9aa 100644 --- a/tests/core/serialize/SelectColumns.cpp +++ b/tests/core/serialize/SelectColumns.cpp @@ -61,7 +61,7 @@ int SelectColumns(int, char*[]) // Optional column manually SQLPP_COMPARE(select(dynamic(true, bar.id)), "SELECT tab_bar.id"); - SQLPP_COMPARE(select(dynamic(false, bar.id)), "SELECT NULL"); + SQLPP_COMPARE(select(dynamic(false, bar.id)), "SELECT NULL as id"); #warning: add more optional column tests diff --git a/tests/core/serialize/clause/CMakeLists.txt b/tests/core/serialize/clause/CMakeLists.txt new file mode 100644 index 00000000..9f288087 --- /dev/null +++ b/tests/core/serialize/clause/CMakeLists.txt @@ -0,0 +1,33 @@ +# 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. + +function(create_test name) + set(target sqlpp11_core_serialize_clause_${name}) + add_executable(${target} ${name}.cpp) + target_link_libraries(${target} PRIVATE sqlpp11::sqlpp11 sqlpp11_testing) + add_test(NAME ${target} COMMAND ${target}) +endfunction() + +create_test(group_by) + diff --git a/tests/core/serialize/clause/group_by.cpp b/tests/core/serialize/clause/group_by.cpp new file mode 100644 index 00000000..6dd850b0 --- /dev/null +++ b/tests/core/serialize/clause/group_by.cpp @@ -0,0 +1,82 @@ +/* + * 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(v); + +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(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"); + + // 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"); + +#warning: Should we prevent the first column to be dynamic? Might be easier to just document it. + // Single dynamic column (this is odd) + SQLPP_COMPARE(group_by(dynamic(true, foo.id)), " GROUP BY tab_foo.id"); + SQLPP_COMPARE(group_by(dynamic(false, foo.id)), " GROUP BY "); // not good + + // Multiple dynamic columns (this is odd if all are dynamic) + SQLPP_COMPARE(group_by(dynamic(true, 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.id, dynamic(true, foo.textNnD), foo.boolN), " GROUP BY tab_foo.id, tab_foo.text_nn_d, tab_foo.bool_n"); + SQLPP_COMPARE(group_by(foo.id, foo.textNnD, dynamic(true, foo.boolN)), " GROUP BY tab_foo.id, tab_foo.text_nn_d, tab_foo.bool_n"); + + SQLPP_COMPARE(group_by(dynamic(false, foo.id), foo.textNnD, foo.boolN), " GROUP BY tab_foo.text_nn_d, tab_foo.bool_n"); + SQLPP_COMPARE(group_by(foo.id, dynamic(false, foo.textNnD), foo.boolN), " GROUP BY tab_foo.id, tab_foo.bool_n"); + SQLPP_COMPARE(group_by(foo.id, foo.textNnD, dynamic(false, foo.boolN)), " GROUP BY tab_foo.id, tab_foo.text_nn_d"); + + SQLPP_COMPARE(group_by(foo.id, dynamic(false, foo.textNnD), dynamic(false, foo.boolN)), " GROUP BY tab_foo.id"); + SQLPP_COMPARE(group_by(dynamic(false, foo.id), foo.textNnD, dynamic(false, foo.boolN)), " GROUP BY tab_foo.text_nn_d"); + SQLPP_COMPARE(group_by(dynamic(false, foo.id), dynamic(false, foo.textNnD), foo.boolN), " GROUP BY tab_foo.bool_n"); + + // Single declared column + SQLPP_COMPARE(group_by(declare_group_by_column(val)), " GROUP BY 17"); + SQLPP_COMPARE(group_by(declare_group_by_column(foo.id + 17)), " GROUP BY tab_foo.id + 17"); + + // Mixed declared column + SQLPP_COMPARE(group_by(foo.id, declare_group_by_column(val)), " GROUP BY tab_foo.id, 17"); + SQLPP_COMPARE(group_by(declare_group_by_column(val), foo.id), " GROUP BY 17, tab_foo.id"); + + // Mixed dynamic declared column + SQLPP_COMPARE(group_by(foo.id, dynamic(true, declare_group_by_column(val))), " GROUP BY tab_foo.id, 17"); + SQLPP_COMPARE(group_by(dynamic(true, declare_group_by_column(val)), foo.id), " GROUP BY 17, tab_foo.id"); + + SQLPP_COMPARE(group_by(foo.id, dynamic(false, declare_group_by_column(val))), " GROUP BY tab_foo.id"); + SQLPP_COMPARE(group_by(dynamic(false, declare_group_by_column(val)), foo.id), " GROUP BY tab_foo.id"); + +#warning add tests with declared columns + + return 0; +} diff --git a/tests/core/serialize/operator/as_expression.cpp b/tests/core/serialize/operator/as_expression.cpp index 8585869b..7330915f 100644 --- a/tests/core/serialize/operator/as_expression.cpp +++ b/tests/core/serialize/operator/as_expression.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "Sample.h" #include "../compare.h" #include @@ -33,6 +34,8 @@ int main(int, char* []) const auto val = sqlpp::value(17); const auto expr = sqlpp::value(17) + 4; + const auto col_id = test::TabFoo{}.id; + SQLPP_COMPARE(val.as(v), "17 AS v"); SQLPP_COMPARE(expr.as(v), "(17 + 4) AS v"); SQLPP_COMPARE(count(val).as(v), "COUNT(17) AS v"); @@ -40,10 +43,12 @@ int main(int, char* []) SQLPP_COMPARE(dynamic(true, val).as(v), "17 AS v"); SQLPP_COMPARE(dynamic(true, expr).as(v), "(17 + 4) AS v"); SQLPP_COMPARE(dynamic(true, count(val)).as(v), "COUNT(17) AS v"); + SQLPP_COMPARE(dynamic(true, col.id).as(v), "tab_foo.id AS v"); SQLPP_COMPARE(dynamic(false, val).as(v), "NULL AS v"); SQLPP_COMPARE(dynamic(false, expr).as(v), "NULL AS v"); SQLPP_COMPARE(dynamic(false, count(val)).as(v), "NULL AS v"); + SQLPP_COMPARE(dynamic(false, col.id).as(v), "NULL AS v"); return 0; } diff --git a/tests/core/serialize/query/CMakeLists.txt b/tests/core/serialize/query/CMakeLists.txt new file mode 100644 index 00000000..a26a562b --- /dev/null +++ b/tests/core/serialize/query/CMakeLists.txt @@ -0,0 +1,33 @@ +# 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. + +function(create_test name) + set(target sqlpp11_core_serialize_query_${name}) + add_executable(${target} ${name}.cpp) + target_link_libraries(${target} PRIVATE sqlpp11::sqlpp11 sqlpp11_testing) + add_test(NAME ${target} COMMAND ${target}) +endfunction() + +create_test(dynamic) + diff --git a/tests/core/serialize/query/dynamic.cpp b/tests/core/serialize/query/dynamic.cpp new file mode 100644 index 00000000..1c3800f2 --- /dev/null +++ b/tests/core/serialize/query/dynamic.cpp @@ -0,0 +1,48 @@ +/* + * 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 + +int main(int, char* []) +{ + const auto val = sqlpp::value(17); + const auto expr = sqlpp::value(17) + 4; + + const auto col_id = test::TabFoo{}.id; + + SQLPP_COMPARE(dynamic(true, val), "17"); + SQLPP_COMPARE(dynamic(true, expr), "17 + 4"); + SQLPP_COMPARE(dynamic(true, count(val)), "COUNT(17)"); + SQLPP_COMPARE(dynamic(true, col_id), "tab_foo.id"); + + SQLPP_COMPARE(dynamic(false, val), "NULL"); + SQLPP_COMPARE(dynamic(false, expr), "NULL"); + SQLPP_COMPARE(dynamic(false, count(val)), "NULL"); + SQLPP_COMPARE(dynamic(false, col_id), "NULL"); + + return 0; +} diff --git a/tests/core/types/clause/select_columns.cpp b/tests/core/types/clause/select_columns.cpp index 4098b99e..265ba676 100644 --- a/tests/core/types/clause/select_columns.cpp +++ b/tests/core/types/clause/select_columns.cpp @@ -53,6 +53,9 @@ void test_select_columns() using knownInt = sqlpp::detail::type_vector; using knownTxt = sqlpp::detail::type_vector; + +#warning test that dynamic join tables are only used by dynamic columns + // Single column. { using T = clause_of_t;