From c2c5f15690ea15b613a9d8c1aec920099d8da93d Mon Sep 17 00:00:00 2001 From: Roland Bock Date: Sat, 13 Jul 2024 13:39:36 +0200 Subject: [PATCH] Add basic serialize functions and start documenting differences --- docs/DifferencesToVersion-1.0.md | 39 +++++ include/sqlpp11/column.h | 5 + include/sqlpp11/functions.h | 1 - include/sqlpp11/is_equal_to_or_null.h | 76 --------- .../sqlpp11/operator/arithmetic_expression.h | 2 +- include/sqlpp11/operator/assign_expression.h | 31 +++- include/sqlpp11/operator/bit_expression.h | 2 +- include/sqlpp11/operator/exists_expression.h | 1 - include/sqlpp11/operator/logical_expression.h | 2 +- include/sqlpp11/serialize.h | 160 ++++++++++++++++++ include/sqlpp11/type_traits.h | 55 ++---- include/sqlpp11/value.h | 11 ++ scripts/ddl2cpp | 4 +- tests/core/types/result_row.cpp | 11 +- tests/core/usage/Sample.h | 28 +-- 15 files changed, 277 insertions(+), 151 deletions(-) create mode 100644 docs/DifferencesToVersion-1.0.md delete mode 100644 include/sqlpp11/is_equal_to_or_null.h diff --git a/docs/DifferencesToVersion-1.0.md b/docs/DifferencesToVersion-1.0.md new file mode 100644 index 00000000..554a36bc --- /dev/null +++ b/docs/DifferencesToVersion-1.0.md @@ -0,0 +1,39 @@ +# New data types + +## optional +Nullable values are represented as `std::optional` if you are using C++17 or later, `sqlpp::compat::optional` otherwise. + +## string_view +Text results are represented as `std::string_view` if you are using C++17 or later, `sqlpp::compat::string_view` otherwise. + +# span +Blob results are represented as `std::span` if you are using C++20 or later, `sqlpp::compat::span` otherwise. + +# Result values +Result rows are represented as structs with the respective columns represented as data members. In version 1.0, these data members used to have a type that wrapped the actual data and provided conversion operators and functions to check for NULL. + +Now data members have the correct data type, e.g. `int64_t` or `optional`. + +# No read-only columns +Version 1.0 had the concept of read-only columns, e.g. you could not modify a column with auto-increment values. + +This concept has been removed. In most cases, you will still not want to modify columns with auto-increment values, but you can do it now, if you want to. + +# IS DISCTINCT FROM +Version 1.0 used to have `is_equal_to_or_null` which translated to either `=` or `IS NULL`. While useful, this did not work with parameters. + +The library now offers `is_distinct_from` and `is_not_distinct_from` which safely compares with actual values and `NULL`. + +# Selecting aggregate functions +In version 1.0, many functions automatically came with a name. This was convenient for selecting, but also rather unspecific, e.g. `select(max(tab.price)).from(tab).unconditionally()` would yield rows with data member called `count_`. + +Now, functions do not come with names. While this will lead to lightly more code, the assumption is that this will lead to slightly more readable code, e.g. + +``` +SQLPP_ALIAS_PROVIDER(max_price); +// ... + for (const auto& row : db(select(max(tab.price)).from(tab).unconditionally())) + { + std::cout << row.max_price << '\n'; + } +``` diff --git a/include/sqlpp11/column.h b/include/sqlpp11/column.h index 005010ea..a3eb099d 100644 --- a/include/sqlpp11/column.h +++ b/include/sqlpp11/column.h @@ -112,6 +112,11 @@ namespace sqlpp using type = typename ColumnSpec::value_type; }; + template + struct has_default> : public ColumnSpec::has_default + { + }; + template Context& serialize(const column_t&, Context& context) { diff --git a/include/sqlpp11/functions.h b/include/sqlpp11/functions.h index b2f0d41e..d4c6eea7 100644 --- a/include/sqlpp11/functions.h +++ b/include/sqlpp11/functions.h @@ -47,7 +47,6 @@ #include #include #include -#include #include namespace sqlpp diff --git a/include/sqlpp11/is_equal_to_or_null.h b/include/sqlpp11/is_equal_to_or_null.h deleted file mode 100644 index 72096b37..00000000 --- a/include/sqlpp11/is_equal_to_or_null.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -/* - * Copyright (c) 2021, 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 - -namespace sqlpp -{ - template - struct is_equal_to_or_null_t: public expression_operators, boolean>, - public alias_operators> - { - using _traits = make_traits; - using _nodes = detail::type_vector>; - - is_equal_to_or_null_t(Expr expr, value_or_null_t value) : _expr(expr), _value(value) - { - } - - is_equal_to_or_null_t(const is_equal_to_or_null_t&) = default; - is_equal_to_or_null_t(is_equal_to_or_null_t&&) = default; - is_equal_to_or_null_t& operator=(const is_equal_to_or_null_t&) = default; - is_equal_to_or_null_t& operator=(is_equal_to_or_null_t&&) = default; - ~is_equal_to_or_null_t() = default; - - - Expr _expr; - value_or_null_t _value; - }; - - template - Context& serialize(const is_equal_to_or_null_t& t, Context& context) - { - if (t._value._is_null) - serialize(t._expr.is_null(), context); - else - serialize(t._expr == t._value, context); - - return context; - } - - template - auto is_equal_to_or_null(Expr expr, value_or_null_t value) -> is_equal_to_or_null_t - { - static_assert(is_expression_t::value, - "is_equal_to_or_null() is to be called an expression (e.g. a column) and a value_or_null expression"); - static_assert(std::is_same, ValueType>::value, - "is_equal_to_or_null() arguments need to have the same value type"); - return {expr, value}; - } - -} // namespace sqlpp diff --git a/include/sqlpp11/operator/arithmetic_expression.h b/include/sqlpp11/operator/arithmetic_expression.h index 1e1eda5d..29d1a6ce 100644 --- a/include/sqlpp11/operator/arithmetic_expression.h +++ b/include/sqlpp11/operator/arithmetic_expression.h @@ -177,7 +177,7 @@ namespace sqlpp }; template - using check_modulus_args = std::enable_if_t::value and has_integral_value::value>; + using check_modulus_args = std::enable_if_t<(is_integral::value or is_unsigned_integral::value) and (is_integral::value or is_unsigned_integral::value)>; template > constexpr auto operator%(L l, R r) -> arithmetic_expression diff --git a/include/sqlpp11/operator/assign_expression.h b/include/sqlpp11/operator/assign_expression.h index 067b5d31..23a81e32 100644 --- a/include/sqlpp11/operator/assign_expression.h +++ b/include/sqlpp11/operator/assign_expression.h @@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include #include #include @@ -40,16 +41,21 @@ namespace sqlpp R value; }; -#warning rename is_column, is_default, values_are_comparable -> values_are_compatible -#warning document the change that there is no read-only! -#warning handle default_t, but only if the column does have a default! template - using check_assign_args = std::enable_if_t::value and values_are_comparable::value and - (can_be_null::value or not is_optional::value) and - (has_default_t::value or not std::is_same::value)>; + using check_assign_args = + std::enable_if_t::value and (can_be_null::value or not is_optional::value)>; - template > - constexpr auto assign(L column, R value) -> assign_expression + template + using check_assign_default_args = std::enable_if_t::value>; + + template , R>> + constexpr auto assign(column_t column, R value) -> assign_expression, R> + { + return {std::move(column), std::move(value)}; + } + + template >> + constexpr auto assign(column_t column, default_value_t value) -> assign_expression, default_value_t> { return {std::move(column), std::move(value)}; } @@ -79,4 +85,13 @@ namespace sqlpp return to_sql_string(context, t.column) + " = " + to_sql_string(context, embrace(t.value)); } */ + + template + Context& serialize(const assign_expression& t, Context& context) + { + serialize(simple_column(t._lhs), context); + context << "="; + serialize_operand(t._rhs, context); + return context; + } } // namespace sqlpp diff --git a/include/sqlpp11/operator/bit_expression.h b/include/sqlpp11/operator/bit_expression.h index 28c12958..c8bbec4a 100644 --- a/include/sqlpp11/operator/bit_expression.h +++ b/include/sqlpp11/operator/bit_expression.h @@ -82,7 +82,7 @@ namespace sqlpp }; template - using check_bit_expression_args = std::enable_if_t::value and has_integral_value::value>; + using check_bit_expression_args = std::enable_if_t::value and (is_integral::value or is_unsigned_integral::value)>; #if 0 template diff --git a/include/sqlpp11/operator/exists_expression.h b/include/sqlpp11/operator/exists_expression.h index 86bf8115..ccf81e8d 100644 --- a/include/sqlpp11/operator/exists_expression.h +++ b/include/sqlpp11/operator/exists_expression.h @@ -45,7 +45,6 @@ namespace sqlpp using type = boolean; }; -#warning: Document that functions dont come with their default alias any more template > constexpr auto exists(SubSelect sub_select) -> exists_t { diff --git a/include/sqlpp11/operator/logical_expression.h b/include/sqlpp11/operator/logical_expression.h index de5ce0da..bd994101 100644 --- a/include/sqlpp11/operator/logical_expression.h +++ b/include/sqlpp11/operator/logical_expression.h @@ -61,7 +61,7 @@ namespace sqlpp }; template - using check_logical_args = std::enable_if_t::value and has_boolean_value::value>; + using check_logical_args = std::enable_if_t::value and is_boolean::value>; template struct value_type_of> diff --git a/include/sqlpp11/serialize.h b/include/sqlpp11/serialize.h index 76f735ab..e108cf40 100644 --- a/include/sqlpp11/serialize.h +++ b/include/sqlpp11/serialize.h @@ -46,4 +46,164 @@ namespace sqlpp return context; } + + template + auto serialize(const bool& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const int8_t& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const int16_t& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const int32_t& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const int64_t& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const uint8_t& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const uint16_t& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const uint32_t& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const uint64_t& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const float& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const double& t, Context& context) -> Context& + { +#warning: Analyze precision. There was a bug about this... + context << t; + return context; + } + + template + auto serialize(const char& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const char* t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const std::string& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + auto serialize(const sqlpp::compat::string_view& t, Context& context) -> Context& + { + context << t; + return context; + } + + template + Context& serialize(const std::vector& t, Context& context) + { + constexpr char hexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + context << "x'"; + for (const auto c : t) + { + context << hexChars[c >> 4] << hexChars[c & 0x0F]; + } + context << '\''; + + return context; + } + + template + Context& serialize(const ::sqlpp::chrono::day_point& t, Context& context) + { + const auto ymd = ::date::year_month_day{t}; + context << "DATE '" << ymd << "'"; + return context; + } + + template + Context& serialize(const std::chrono::microseconds& t, Context& context) + { + context << '\'' << ::date::make_time(t) << '\''; + return context; + } + + template + Context& serialize(const std::chrono::time_point& t, Context& context) + { + const auto dp = ::sqlpp::chrono::floor<::date::days>(t); + const auto time = ::date::make_time(t - dp); + const auto ymd = ::date::year_month_day{dp}; + context << "TIMESTAMP '" << ymd << ' ' << time << "'"; + return context; + } + + template + auto serialize(const sqlpp::compat::optional& t, Context& context) -> Context& + { + if (not t.has_value()) + { + context << "NULL"; + } + else + { + serialize(*t, context); + } + return context; + } + } // namespace sqlpp diff --git a/include/sqlpp11/type_traits.h b/include/sqlpp11/type_traits.h index fac9f01c..e5672b59 100644 --- a/include/sqlpp11/type_traits.h +++ b/include/sqlpp11/type_traits.h @@ -115,7 +115,6 @@ namespace sqlpp struct no_value_t; - // This requires specializations for anything that has a value, like a column or a boolean expression template struct value_type_of { @@ -134,6 +133,11 @@ namespace sqlpp template struct has_value_type : public std::integral_constant, no_value_t>::value> {}; + template + struct has_default : public std::false_type + { + }; + template struct is_not_cpp_bool_t { @@ -181,7 +185,6 @@ namespace sqlpp template <> struct value_type_of { using type = text; }; - ///////////////// struct blob; template <> struct value_type_of> { using type = blob; }; @@ -200,63 +203,39 @@ namespace sqlpp struct time_point; template struct value_type_of> { using type = time_point; }; - ///////////////// - - template - struct is_boolean : public std::is_same + struct is_boolean : public std::is_same>, boolean> { }; - template <> - struct is_boolean : public std::true_type { - }; - template <> struct is_boolean : public std::true_type { }; template - struct has_boolean_value : public std::integral_constant>::value or - is_boolean>>::value> - { - }; - - struct integral; - template - struct is_integral : public std::is_integral{}; - - template <> - struct is_integral : public std::false_type // char is text + struct is_integral : public std::is_same>, integral> { }; template <> - struct is_integral : public std::false_type // bool is boolean - { + struct is_integral : public std::true_type { }; - template <> - struct is_integral : public std::true_type{}; - - template <> - struct is_integral : public std::true_type{}; - template - struct has_integral_value - : public std::integral_constant>::value or - is_integral>>::value> + struct is_unsigned_integral : public std::is_same>, unsigned_integral> { }; + template <> + struct is_unsigned_integral : public std::true_type { + }; + // A generic numeric type which could be (unsigned) integral or floating point. struct numeric; template - struct is_numeric : public std::integral_constant::value or std::is_floating_point::value>{}; + struct is_numeric : public std::integral_constant::value or is_unsigned_integral::value or std::is_floating_point::value>{}; template <> struct is_numeric : public std::true_type{}; @@ -375,7 +354,7 @@ namespace sqlpp struct values_are_comparable : public std::integral_constant::value and has_blob_value::value) or - (has_boolean_value::value and has_boolean_value::value) or + (is_boolean::value and is_boolean::value) or (has_day_point_value::value and has_day_point_value::value) or (has_numeric_value::value and has_numeric_value::value) or (has_text_value::value and has_text_value::value) or @@ -421,6 +400,7 @@ namespace sqlpp using is_time_point_t = std::is_same, time_point>; // joined data type +#warning: These something_t data type classifiers should be removed template using is_numeric_t = logic::any_t::value, is_unsigned_integral_t::value, is_floating_point_t::value>; @@ -469,9 +449,6 @@ namespace sqlpp SQLPP_VALUE_TRAIT_GENERATOR(is_union_flag) SQLPP_VALUE_TRAIT_GENERATOR(is_result_field) -#warning Document loss of must_not_insert/update and require_insert (and new has_default) - SQLPP_VALUE_TRAIT_GENERATOR(has_default) - SQLPP_VALUE_TRAIT_GENERATOR(is_statement) SQLPP_VALUE_TRAIT_GENERATOR(is_prepared_statement) SQLPP_VALUE_TRAIT_GENERATOR(is_union) diff --git a/include/sqlpp11/value.h b/include/sqlpp11/value.h index c35ae240..447389ad 100644 --- a/include/sqlpp11/value.h +++ b/include/sqlpp11/value.h @@ -43,6 +43,7 @@ namespace sqlpp T _value; }; + template struct value_type_of> { @@ -52,9 +53,19 @@ namespace sqlpp template using check_value_arg = std::enable_if_t, no_value_t>::value and values_are_comparable::value>; + template + Context& serialize(const value_t& t, Context& context) + { +#warning: Untested + serialize(t._value, context); + + return context; + } + template > auto value(T t) -> value_t { return {std::move(t)}; } + } // namespace sqlpp diff --git a/scripts/ddl2cpp b/scripts/ddl2cpp index 517b1753..af50ad01 100755 --- a/scripts/ddl2cpp +++ b/scripts/ddl2cpp @@ -743,9 +743,9 @@ def createHeader(): column.hasAutoValue or \ (autoId and sqlColumnName == "id") if columnHasDefault: - print(" using has_default_value = std::true_type;", file=header) + print(" using has_default = std::true_type;", file=header) else: - print(" using has_default_value = std::false_type;", file=header) + print(" using has_default = std::false_type;", file=header) print(" };", file=header) print(" } // namespace " + tableNamespace, file=header) print("", file=header) diff --git a/tests/core/types/result_row.cpp b/tests/core/types/result_row.cpp index be0dd214..9981231b 100644 --- a/tests/core/types/result_row.cpp +++ b/tests/core/types/result_row.cpp @@ -106,8 +106,9 @@ int main() static_assert(is_same_type>(), ""); } - static_assert(sqlpp::has_boolean_value::value, ""); - static_assert(sqlpp::has_boolean_value::value, ""); +#warning: also test with optional + static_assert(sqlpp::is_boolean::value, ""); + static_assert(sqlpp::is_boolean::value, ""); static_assert(sqlpp::has_text_value::value, ""); static_assert(sqlpp::has_text_value::value, ""); static_assert(sqlpp::has_numeric_value::value, ""); @@ -257,8 +258,6 @@ int main() sqlpp::numeric>::value, ""); -#warning: Is bit-shifting with NULL "legal"? probably. -#warning: Note that bit-shifting is defined for signed int only // Bit shifting combining optional value with non-optional value yields optional boolean. static_assert(std::is_same, sqlpp::compat::optional>::value, @@ -293,7 +292,7 @@ int main() // assignment is no value static_assert(std::is_same, sqlpp::no_value_t>::value, ""); -#warning: This is not the real thing yet + static_assert(std::is_same, sqlpp::no_value_t>::value, ""); // as expressions retain the value type of the real thing static_assert(std::is_same, sqlpp::value_type_of_t>::value, ""); @@ -304,8 +303,6 @@ int main() static_assert(std::is_same, sqlpp::compat::optional>::value, ""); -#warning: No magic for NULL in operators, e.g. comparison. It might therefore be reasonable to disallow comparison with optoinal values? But then again, columns can also be NULL, failing to compare to anything. In any case, do not translate `a == nullopt` to `a IS NULL`. Same for parameters. - #if 0 diff --git a/tests/core/usage/Sample.h b/tests/core/usage/Sample.h index 220e9a28..71095ddc 100644 --- a/tests/core/usage/Sample.h +++ b/tests/core/usage/Sample.h @@ -25,7 +25,7 @@ namespace test }; }; using value_type = ::sqlpp::integer; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct TextNnD { @@ -42,7 +42,7 @@ namespace test }; }; using value_type = ::sqlpp::text; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct IntN { @@ -59,7 +59,7 @@ namespace test }; }; using value_type = ::sqlpp::compat::optional<::sqlpp::integer>; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct DoubleN { @@ -76,7 +76,7 @@ namespace test }; }; using value_type = ::sqlpp::compat::optional<::sqlpp::floating_point>; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct UIntN { @@ -93,7 +93,7 @@ namespace test }; }; using value_type = ::sqlpp::compat::optional<::sqlpp::integer_unsigned>; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct BlobN { @@ -110,7 +110,7 @@ namespace test }; }; using value_type = ::sqlpp::compat::optional<::sqlpp::blob>; - using has_default_value = std::true_type; + using has_default = std::true_type; }; } // namespace TabFoo_ @@ -152,7 +152,7 @@ namespace test }; }; using value_type = ::sqlpp::integer; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct TextN { @@ -169,7 +169,7 @@ namespace test }; }; using value_type = ::sqlpp::compat::optional<::sqlpp::text>; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct BoolNn { @@ -186,7 +186,7 @@ namespace test }; }; using value_type = ::sqlpp::boolean; - using has_default_value = std::false_type; + using has_default = std::false_type; }; struct IntN { @@ -203,7 +203,7 @@ namespace test }; }; using value_type = ::sqlpp::compat::optional<::sqlpp::integer>; - using has_default_value = std::true_type; + using has_default = std::true_type; }; } // namespace TabBar_ @@ -243,7 +243,7 @@ namespace test }; }; using value_type = ::sqlpp::integer; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct DayPointN { @@ -260,7 +260,7 @@ namespace test }; }; using value_type = ::sqlpp::compat::optional<::sqlpp::day_point>; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct TimePointN { @@ -277,7 +277,7 @@ namespace test }; }; using value_type = ::sqlpp::compat::optional<::sqlpp::time_point>; - using has_default_value = std::true_type; + using has_default = std::true_type; }; struct TimeOfDayN { @@ -294,7 +294,7 @@ namespace test }; }; using value_type = ::sqlpp::compat::optional<::sqlpp::time_of_day>; - using has_default_value = std::true_type; + using has_default = std::true_type; }; } // namespace TabDateTime_