0
0
mirror of https://github.com/rbock/sqlpp11.git synced 2024-11-16 04:47:18 +08:00

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.
This commit is contained in:
rbock 2015-10-29 15:51:26 +01:00
parent 73e8f04127
commit 5e83929555
7 changed files with 101 additions and 37 deletions

View File

@ -62,6 +62,8 @@ namespace sqlpp
template <typename T>
using _is_valid_operand = is_valid_operand<value_type_of<ColumnSpec>, T>;
template <typename T>
using _is_valid_assignment_operand = is_valid_assignment_operand<value_type_of<ColumnSpec>, T>;
column_t() = default;
column_t(const column_t&) = default;
@ -87,7 +89,7 @@ namespace sqlpp
auto operator=(T t) const -> assignment_t<column_t, wrap_operand_t<T>>
{
using rhs = wrap_operand_t<T>;
static_assert(_is_valid_operand<rhs>::value, "invalid rhs assignment operand");
static_assert(_is_valid_assignment_operand<rhs>::value, "invalid rhs assignment operand");
return {*this, {rhs{t}}};
}

View File

@ -28,7 +28,7 @@
#define SQLPP_DATE_H
#include <date.h>
#include <sqlpp11/date_time_fwd.h>
#include <sqlpp11/date_time.h>
#include <sqlpp11/basic_expression_operators.h>
#include <sqlpp11/type_traits.h>
#include <sqlpp11/exception.h>
@ -43,10 +43,12 @@ namespace sqlpp
{
using _traits = make_traits<date, tag::is_value_type>;
using _tag = tag::is_date;
using _cpp_value_type = cpp::day_point;
using _cpp_value_type = ::sqlpp::chrono::day_point;
template <typename T>
using _is_valid_operand = is_time_point_t<T>;
template <typename T>
using _is_valid_assignment_operand = is_date_t<T>;
};
// date parameter value
@ -194,7 +196,7 @@ namespace sqlpp
template <typename Target>
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:

View File

@ -42,7 +42,7 @@ namespace sqlpp
{
using _traits = make_traits<date_time, tag::is_value_type>;
using _tag = tag::is_date_time;
using _cpp_value_type = cpp::ms_point;
using _cpp_value_type = ::sqlpp::chrono::mus_point;
template <typename T>
using _is_valid_operand = is_time_point_t<T>;
@ -109,7 +109,7 @@ namespace sqlpp
template <typename Target>
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 <typename Target>
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;
}

View File

@ -31,16 +31,16 @@
namespace sqlpp
{
namespace cpp
namespace chrono
{
using days = std::chrono::duration<int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
using day_point = std::chrono::time_point<std::chrono::system_clock, days>;
using ms_point = std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds>;
using mus_point = std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds>;
}
struct date;
struct date_time;
};
}
#endif

View File

@ -58,6 +58,27 @@ namespace sqlpp
and ValueType::template _is_valid_operand<T>::value // the correct value type is required, of course
;
};
template <typename ValueType, typename T, typename Enable = void>
struct is_valid_assignment_operand
{
static constexpr bool value =
is_expression_t<T>::value // expressions are OK
and ValueType::template _is_valid_operand<T>::value // the correct value type is required, of course
;
};
template <typename ValueType, typename T>
struct is_valid_assignment_operand<
ValueType,
T,
typename std::enable_if<std::is_class<typename ValueType::template _is_valid_assignment_operand<T>>::value>::type>
{
static constexpr bool value =
is_expression_t<T>::value // expressions are OK
and ValueType::template _is_valid_assignment_operand<T>::value // the correct value type is required, of course
;
};
}
#endif

View File

@ -85,10 +85,54 @@ namespace sqlpp
}
};
struct date_operand : public alias_operators<date_operand>
{
using _traits = make_traits<date, tag::is_expression, tag::is_wrapped_value>;
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 <typename Context>
struct serializer_t<Context, date_operand>
{
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 <typename Period>
struct date_time_operand : public alias_operators<date_time_operand<Period>>
{
using _traits = make_traits<date, tag::is_expression, tag::is_wrapped_value>;
using _traits = make_traits<date_time, tag::is_expression, tag::is_wrapped_value>;
using _nodes = detail::type_vector<>;
using _is_aggregate_expression = std::true_type;
@ -132,20 +176,6 @@ namespace sqlpp
}
};
template <typename Context>
struct serializer_t<Context, date_time_operand<cpp::days>>
{
using _serialize_check = consistent_t;
using Operand = date_time_operand<cpp::days>;
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<integral_operand>
{
using _traits = make_traits<integral, tag::is_expression, tag::is_wrapped_value>;
@ -293,6 +323,12 @@ namespace sqlpp
using type = date_time_operand<Period>;
};
template <>
struct wrap_operand<std::chrono::time_point<std::chrono::system_clock, sqlpp::chrono::days>, void>
{
using type = date_operand;
};
template <typename T>
struct wrap_operand<T, typename std::enable_if<std::is_integral<T>::value>::type>
{

View File

@ -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<sqlpp::cpp::days>(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<sqlpp::cpp::days>(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<sqlpp::cpp::days>(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<sqlpp::cpp::days>(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<sqlpp::cpp::days>(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<sqlpp::cpp::days>(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;