From 5e83929555658d44e5695c881d2be4659e00693f Mon Sep 17 00:00:00 2001 From: rbock Date: Thu, 29 Oct 2015 15:51:26 +0100 Subject: [PATCH] Date/time adjustments, including date cannot be assigned with timestamp This is inspired by sqlite's behaviour: If you have a date column (say colDate) and update it with colDate = DATETIME('2015-01-01T20:20:20); colDate will contain the date time, not just the date (while the connector would probably just read the date part). In order to prevent this kind of inconsistencies, date can be assigned only with dates, not with timestamps. --- include/sqlpp11/column.h | 4 +- include/sqlpp11/date.h | 8 ++-- include/sqlpp11/date_time.h | 10 ++--- include/sqlpp11/date_time_fwd.h | 6 +-- include/sqlpp11/value_type_fwd.h | 21 ++++++++++ include/sqlpp11/wrap_operand.h | 66 ++++++++++++++++++++++++-------- tests/DateTime.cpp | 23 ++++++----- 7 files changed, 101 insertions(+), 37 deletions(-) diff --git a/include/sqlpp11/column.h b/include/sqlpp11/column.h index 25d86233..22842dcc 100644 --- a/include/sqlpp11/column.h +++ b/include/sqlpp11/column.h @@ -62,6 +62,8 @@ namespace sqlpp template using _is_valid_operand = is_valid_operand, T>; + template + using _is_valid_assignment_operand = is_valid_assignment_operand, T>; column_t() = default; column_t(const column_t&) = default; @@ -87,7 +89,7 @@ namespace sqlpp auto operator=(T t) const -> assignment_t> { using rhs = wrap_operand_t; - static_assert(_is_valid_operand::value, "invalid rhs assignment operand"); + static_assert(_is_valid_assignment_operand::value, "invalid rhs assignment operand"); return {*this, {rhs{t}}}; } diff --git a/include/sqlpp11/date.h b/include/sqlpp11/date.h index 37eb170c..ce1d645a 100644 --- a/include/sqlpp11/date.h +++ b/include/sqlpp11/date.h @@ -28,7 +28,7 @@ #define SQLPP_DATE_H #include -#include +#include #include #include #include @@ -43,10 +43,12 @@ namespace sqlpp { using _traits = make_traits; using _tag = tag::is_date; - using _cpp_value_type = cpp::day_point; + using _cpp_value_type = ::sqlpp::chrono::day_point; template using _is_valid_operand = is_time_point_t; + template + using _is_valid_assignment_operand = is_date_t; }; // date parameter value @@ -194,7 +196,7 @@ namespace sqlpp template void _bind(Target& target, size_t i) { - target._bind_day_point_result(i, &_value, &_is_null); + target._bind_date_result(i, &_value, &_is_null); } private: diff --git a/include/sqlpp11/date_time.h b/include/sqlpp11/date_time.h index 1805b334..9b1933ea 100644 --- a/include/sqlpp11/date_time.h +++ b/include/sqlpp11/date_time.h @@ -42,7 +42,7 @@ namespace sqlpp { using _traits = make_traits; using _tag = tag::is_date_time; - using _cpp_value_type = cpp::ms_point; + using _cpp_value_type = ::sqlpp::chrono::mus_point; template using _is_valid_operand = is_time_point_t; @@ -109,7 +109,7 @@ namespace sqlpp template void _bind(Target& target, size_t index) const { - target._bind_date_parameter(index, &_value, _is_null); + target._bind_date_time_parameter(index, &_value, _is_null); } private: @@ -194,7 +194,7 @@ namespace sqlpp template void _bind(Target& target, size_t i) { - target._bind_date_result(i, &_value, &_is_null); + target._bind_date_time_result(i, &_value, &_is_null); } private: @@ -217,8 +217,8 @@ namespace sqlpp } else { - const auto dp = ::date::floor<::date::days>(t); - const auto time = ::date::make_time(t - dp); + const auto dp = ::date::floor<::date::days>(t.value()); + const auto time = ::date::make_time(t.value() - dp); const auto ymd = ::date::year_month_day{dp}; context << ymd << ' ' << time; } diff --git a/include/sqlpp11/date_time_fwd.h b/include/sqlpp11/date_time_fwd.h index 8ed68c38..9ca9be00 100644 --- a/include/sqlpp11/date_time_fwd.h +++ b/include/sqlpp11/date_time_fwd.h @@ -31,16 +31,16 @@ namespace sqlpp { - namespace cpp + namespace chrono { using days = std::chrono::duration, std::chrono::hours::period>>; using day_point = std::chrono::time_point; - using ms_point = std::chrono::time_point; + using mus_point = std::chrono::time_point; } struct date; struct date_time; -}; +} #endif diff --git a/include/sqlpp11/value_type_fwd.h b/include/sqlpp11/value_type_fwd.h index 4448796c..2923d896 100644 --- a/include/sqlpp11/value_type_fwd.h +++ b/include/sqlpp11/value_type_fwd.h @@ -58,6 +58,27 @@ namespace sqlpp and ValueType::template _is_valid_operand::value // the correct value type is required, of course ; }; + + template + struct is_valid_assignment_operand + { + static constexpr bool value = + is_expression_t::value // expressions are OK + and ValueType::template _is_valid_operand::value // the correct value type is required, of course + ; + }; + + template + struct is_valid_assignment_operand< + ValueType, + T, + typename std::enable_if>::value>::type> + { + static constexpr bool value = + is_expression_t::value // expressions are OK + and ValueType::template _is_valid_assignment_operand::value // the correct value type is required, of course + ; + }; } #endif diff --git a/include/sqlpp11/wrap_operand.h b/include/sqlpp11/wrap_operand.h index 013d6667..92925b2e 100644 --- a/include/sqlpp11/wrap_operand.h +++ b/include/sqlpp11/wrap_operand.h @@ -85,10 +85,54 @@ namespace sqlpp } }; + struct date_operand : public alias_operators + { + using _traits = make_traits; + using _nodes = detail::type_vector<>; + using _is_aggregate_expression = std::true_type; + + using _value_t = ::sqlpp::chrono::day_point; + + date_operand() : _t{} + { + } + + date_operand(_value_t t) : _t(t) + { + } + + date_operand(const date_operand&) = default; + date_operand(date_operand&&) = default; + date_operand& operator=(const date_operand&) = default; + date_operand& operator=(date_operand&&) = default; + ~date_operand() = default; + + bool _is_trivial() const + { + return _t == _value_t{}; + } + + _value_t _t; + }; + + template + struct serializer_t + { + using _serialize_check = consistent_t; + using Operand = date_operand; + + static Context& _(const Operand& t, Context& context) + { + const auto ymd = ::date::year_month_day{t._t}; + context << "DATE '" << ymd << "'"; + return context; + } + }; + template struct date_time_operand : public alias_operators> { - using _traits = make_traits; + using _traits = make_traits; using _nodes = detail::type_vector<>; using _is_aggregate_expression = std::true_type; @@ -132,20 +176,6 @@ namespace sqlpp } }; - template - struct serializer_t> - { - using _serialize_check = consistent_t; - using Operand = date_time_operand; - - static Context& _(const Operand& t, Context& context) - { - const auto ymd = ::date::year_month_day{t._t}; - context << "DATE '" << ymd << "'"; - return context; - } - }; - struct integral_operand : public alias_operators { using _traits = make_traits; @@ -293,6 +323,12 @@ namespace sqlpp using type = date_time_operand; }; + template <> + struct wrap_operand, void> + { + using type = date_operand; + }; + template struct wrap_operand::value>::type> { diff --git a/tests/DateTime.cpp b/tests/DateTime.cpp index 2537bf32..1b76446e 100644 --- a/tests/DateTime.cpp +++ b/tests/DateTime.cpp @@ -36,32 +36,35 @@ int DateTime(int, char**) MockDb::_serializer_context_t printer; test::TabDateTime t; - for (const auto& row : db(select(sqlpp::value(std::chrono::system_clock::now()).as(now)))) + for (const auto& row : db(select(::sqlpp::value(std::chrono::system_clock::now()).as(now)))) { std::cout << row.now; } + for (const auto& row : db(select(all_of(t)).from(t).where(true))) + { + std::cout << row.colDate; + std::cout << row.colDateTime; + } printer.reset(); - std::cerr << serialize(sqlpp::value(std::chrono::system_clock::now()), printer).str() << std::endl; + std::cerr << serialize(::sqlpp::value(std::chrono::system_clock::now()), printer).str() << std::endl; - db(insert_into(t).set(t.colDate = ::date::floor(std::chrono::system_clock::now()))); - db(insert_into(t).set(t.colDate = std::chrono::system_clock::now())); - db(insert_into(t).set(t.colDateTime = ::date::floor(std::chrono::system_clock::now()))); + db(insert_into(t).set(t.colDate = ::date::floor<::sqlpp::chrono::days>(std::chrono::system_clock::now()))); + db(insert_into(t).set(t.colDateTime = ::date::floor<::sqlpp::chrono::days>(std::chrono::system_clock::now()))); db(insert_into(t).set(t.colDateTime = std::chrono::system_clock::now())); db(update(t) - .set(t.colDate = ::date::floor(std::chrono::system_clock::now())) + .set(t.colDate = ::date::floor<::sqlpp::chrono::days>(std::chrono::system_clock::now())) .where(t.colDate < std::chrono::system_clock::now())); - db(update(t).set(t.colDate = std::chrono::system_clock::now()).where(t.colDate < std::chrono::system_clock::now())); db(update(t) - .set(t.colDateTime = ::date::floor(std::chrono::system_clock::now())) + .set(t.colDateTime = ::date::floor<::sqlpp::chrono::days>(std::chrono::system_clock::now())) .where(t.colDate < std::chrono::system_clock::now())); db(update(t) .set(t.colDateTime = std::chrono::system_clock::now()) .where(t.colDate < std::chrono::system_clock::now())); - db(remove_from(t).where(t.colDate == ::date::floor(std::chrono::system_clock::now()))); + db(remove_from(t).where(t.colDate == ::date::floor<::sqlpp::chrono::days>(std::chrono::system_clock::now()))); db(remove_from(t).where(t.colDate == std::chrono::system_clock::now())); - db(remove_from(t).where(t.colDateTime == ::date::floor(std::chrono::system_clock::now()))); + db(remove_from(t).where(t.colDateTime == ::date::floor<::sqlpp::chrono::days>(std::chrono::system_clock::now()))); db(remove_from(t).where(t.colDateTime == std::chrono::system_clock::now())); return 0;