summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2023-02-14 11:52:10 +0400
committerAlexander Barkov <bar@mariadb.com>2023-02-14 11:55:26 +0400
commitd3d9514dd9f752f682024197463abc233a3008bd (patch)
tree54724aa95ab07ee8cca0b15bc4aeadf6c34deb1a /sql
parent8c1ad2a9fe940376d7cb79515685138e3591e5b7 (diff)
downloadmariadb-git-bb-11.0-bar-MDEV-15750.tar.gz
MDEV-15750 preserve MYSQL_TYPE_TIMESTAMP in temporal arithmeticbb-11.0-bar-MDEV-15750
In progress.
Diffstat (limited to 'sql')
-rw-r--r--sql/item_func.h62
-rw-r--r--sql/item_timefunc.cc237
-rw-r--r--sql/item_timefunc.h64
-rw-r--r--sql/sql_time.cc9
-rw-r--r--sql/sql_type.cc80
-rw-r--r--sql/sql_type.h151
-rw-r--r--sql/structs.h23
7 files changed, 572 insertions, 54 deletions
diff --git a/sql/item_func.h b/sql/item_func.h
index 520dbdc90c7..22ffda60111 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -661,6 +661,52 @@ public:
};
+ class Handler_timestamp: public Handler_temporal
+ {
+ protected:
+ Datetime to_datetime(THD *thd, Item_handled_func *item) const
+ {
+ Timestamp_or_zero_datetime_native_null ts(thd, item);
+ return Timestamp_or_zero_datetime(ts).to_datetime(thd);
+ }
+ public:
+ const Type_handler *return_type_handler(const Item_handled_func *) const
+ {
+ return &type_handler_timestamp2;
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const
+ {
+ // QQ: check fuzzydate???
+ return item->null_value= to_datetime(thd, item).copy_to_mysql_time(ltime);
+ }
+ double val_real(Item_handled_func *item) const
+ {
+ Datetime dt= to_datetime(current_thd, item);
+ item->null_value= !dt.is_valid_datetime();
+ return dt.to_double();
+ }
+ longlong val_int(Item_handled_func *item) const
+ {
+ Datetime dt= to_datetime(current_thd, item);
+ item->null_value= !dt.is_valid_datetime();
+ return dt.to_longlong();
+ }
+ my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const
+ {
+ Datetime dt= to_datetime(current_thd, item);
+ item->null_value= !dt.is_valid_datetime();
+ return dt.to_decimal(to);
+ }
+ String *val_str_ascii(Item_handled_func *item, String *to) const
+ {
+ Datetime dt= to_datetime(current_thd, item);
+ item->null_value= !dt.is_valid_datetime();
+ return dt.to_string(to, item->decimals);
+ }
+ };
+
+
class Handler_int: public Handler
{
public:
@@ -726,6 +772,22 @@ public:
}
};
+ class Handler_slonglong: public Handler_int
+ {
+ public:
+ const Type_handler *return_type_handler(const Item_handled_func *item) const
+ {
+ return &type_handler_slonglong;
+ }
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ item->unsigned_flag= false;
+ item->collation= DTCollation_numeric();
+ item->fix_char_length(21);
+ return false;
+ }
+ };
+
class Handler_ulonglong: public Handler_int
{
public:
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 52b9ae7a682..9f0cbba472e 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -56,17 +56,24 @@
#include <m_ctype.h>
#include <time.h>
-/** Day number for Dec 31st, 9999. */
-#define MAX_DAY_NUMBER 3652424L
Func_handler_date_add_interval_datetime_arg0_time
func_handler_date_add_interval_datetime_arg0_time;
Func_handler_date_add_interval_datetime func_handler_date_add_interval_datetime;
+
+Func_handler_date_add_interval_timestamp_is_timestamp_plus_YYYYMM
+ func_handler_date_add_interval_timestamp_is_timestamp_plus_YYYYMM;
+
+Func_handler_date_add_interval_timestamp_is_timestamp_plus_DDhhmmss
+ func_handler_date_add_interval_timestamp_is_timestamp_plus_DDhhmmss;
+
Func_handler_date_add_interval_date func_handler_date_add_interval_date;
Func_handler_date_add_interval_time func_handler_date_add_interval_time;
Func_handler_date_add_interval_string func_handler_date_add_interval_string;
+Func_handler_add_time_timestamp func_handler_add_time_timestamp_add(1);
+Func_handler_add_time_timestamp func_handler_add_time_timestamp_sub(-1);
Func_handler_add_time_datetime func_handler_add_time_datetime_add(1);
Func_handler_add_time_datetime func_handler_add_time_datetime_sub(-1);
Func_handler_add_time_time func_handler_add_time_time_add(1);
@@ -2848,11 +2855,25 @@ bool Item_date_add_interval::fix_length_and_dec(THD *thd)
*/
arg0_field_type= args[0]->field_type();
- if (arg0_field_type == MYSQL_TYPE_DATETIME ||
- arg0_field_type == MYSQL_TYPE_TIMESTAMP)
+ if (arg0_field_type == MYSQL_TYPE_DATETIME)
{
set_func_handler(&func_handler_date_add_interval_datetime);
}
+ else if (arg0_field_type == MYSQL_TYPE_TIMESTAMP)
+ {
+ switch (int_type) {
+ case INTERVAL_YEAR:
+ case INTERVAL_QUARTER:
+ case INTERVAL_MONTH:
+ case INTERVAL_YEAR_MONTH:
+ set_func_handler(
+ &func_handler_date_add_interval_timestamp_is_timestamp_plus_YYYYMM);
+ break;
+ default:
+ set_func_handler(
+ &func_handler_date_add_interval_timestamp_is_timestamp_plus_DDhhmmss);
+ }
+ }
else if (arg0_field_type == MYSQL_TYPE_DATE)
{
if (int_type <= INTERVAL_DAY || int_type == INTERVAL_YEAR_MONTH)
@@ -2876,6 +2897,72 @@ bool Item_date_add_interval::fix_length_and_dec(THD *thd)
}
+bool Func_handler_date_add_interval_timestamp_is_timestamp_plus_YYYYMM
+ ::val_native(THD *thd, Item_handled_func *func, Native *to) const
+{
+ Timestamp_null ts(thd, func->arguments()[0], false);
+ if ((func->null_value= ts.is_null()))
+ return true;
+ INTERVAL interval;
+ if (get_interval_value(thd, func->arguments()[1], int_type(func), &interval))
+ return func->null_value= true;
+
+ if (sub(func))
+ interval.neg = !interval.neg;
+
+ return func->null_value= ts.add(thd, int_type(func), interval) ||
+ ts.check_zero_timestamp_result_with_warn(thd) ||
+ ts.to_native(to, func->decimals);
+}
+
+
+bool Func_handler_date_add_interval_timestamp_is_timestamp_plus_DDhhmmss
+ ::val_native(THD *thd, Item_handled_func *func, Native *to) const
+{
+ Timestamp_null ts(thd, func->arguments()[0], false);
+ if ((func->null_value= ts.is_null()))
+ return true;
+ INTERVAL interval;
+ if (get_interval_value(thd, func->arguments()[1], int_type(func), &interval))
+ return func->null_value= true;
+
+ // Avoid overflow in INTERVAL::unsigned_DDhhmmssff_to_seconds()
+ if (interval.DDhhmmssff_to_days_abs() > Date::MAX_DAY_NUMBER())
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_DATETIME_FUNCTION_OVERFLOW,
+ ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW),
+ "interval");
+ return func->null_value= true;
+ }
+
+ Sec6 sec6(interval.neg, interval.DDhhmmssff_to_integer_seconds_abs(),
+ interval.second_part % 1000000);
+
+ return func->null_value=
+ (sub(func) ? ts.sub(thd, sec6) : ts.add(thd, sec6)) ||
+ ts.check_zero_timestamp_result_with_warn(thd) ||
+ ts.to_native(to, func->decimals);
+}
+
+
+bool Func_handler_add_time_timestamp
+ ::val_native(THD *thd, Item_handled_func *func, Native *to) const
+{
+ Timestamp_null ts(thd, func->arguments()[0], false);
+ if (ts.is_null())
+ return func->null_value= true;
+ Interval_DDhhmmssff it(thd, func->arguments()[1]);
+ if (!it.is_valid_interval_DDhhmmssff())
+ return (func->null_value= true);
+ return func->null_value=
+ (m_sign < 0 ? ts.sub(thd, it.to_sec6()) :
+ ts.add(thd, it.to_sec6())) ||
+ ts.check_zero_timestamp_result_with_warn(thd) ||
+ ts.to_native(to, func->decimals);
+}
+
+
bool Func_handler_date_add_interval_datetime_arg0_time::
get_date(THD *thd, Item_handled_func *item,
MYSQL_TIME *to, date_mode_t fuzzy) const
@@ -3467,12 +3554,16 @@ bool Item_func_add_time::fix_length_and_dec(THD *thd)
arg0_field_type= args[0]->field_type();
if (arg0_field_type == MYSQL_TYPE_DATE ||
- arg0_field_type == MYSQL_TYPE_DATETIME ||
- arg0_field_type == MYSQL_TYPE_TIMESTAMP)
+ arg0_field_type == MYSQL_TYPE_DATETIME)
{
set_func_handler(sign > 0 ? &func_handler_add_time_datetime_add :
&func_handler_add_time_datetime_sub);
}
+ else if (arg0_field_type == MYSQL_TYPE_TIMESTAMP)
+ {
+ set_func_handler(sign > 0 ? &func_handler_add_time_timestamp_add :
+ &func_handler_add_time_timestamp_sub);
+ }
else if (arg0_field_type == MYSQL_TYPE_TIME)
{
set_func_handler(sign > 0 ? &func_handler_add_time_time_add :
@@ -3580,12 +3671,71 @@ longlong Item_func_microsecond::val_int()
}
-longlong Item_func_timestamp_diff::val_int()
+/*
+ Generic TIMESTAMPDIFF implementation:
+ TIMESTAMPDIFF(int_type, datetime, datetime)
+ TIMESTAMPDIFF(int_type, datetime, timestamp)
+ TIMESTAMPDIFF(int_type, timestamp, datetime)
+
+ Special case:
+ TIMESTAMPDIFF(special_timestamp2_int_type, timestamp, timestamp)
+ special_timestamp2_int_type := YEAR | QUARTER | MONTH
+
+ All non-datetime arguments are converted to datetime before calculating.
+ DST changes for timestamp arguments are not taken into account.
+*/
+class Func_handler_datetime_diff: public Item_handled_func::Handler_slonglong
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *func) const override
+ {
+ func->set_maybe_null();
+ return Handler_slonglong::fix_length_and_dec(func);
+ }
+ Longlong_null to_longlong_null(Item_handled_func *func) const override
+ {
+ return static_cast<Item_func_timestamp_diff*>(func)->datetime_diff();
+ }
+};
+
+
+/*
+ Fast and DST-change aware TIMESTAMPDIFF implementation for two
+ TIMESTAMP arguments in combination with the interval type which
+ does not need year or month for calculations:
+
+ TIMESTAMPDIFF(int_type, timestamp, timestamp)
+ int_type := WEEK | DAY | HOUR | MINUTE | SECOND | MICROSECOND
+*/
+class Func_handler_timestamp_diff: public Item_handled_func::Handler_slonglong
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *func) const override
+ {
+ func->set_maybe_null();
+ return Handler_slonglong::fix_length_and_dec(func);
+ }
+ Longlong_null to_longlong_null(Item_handled_func *func) const override
+ {
+ THD *thd= current_thd;
+ Timestamp_null expr1(thd, func->arguments()[0], false);
+ if (expr1.is_null())
+ return Longlong_null();
+ Timestamp_null expr2(thd, func->arguments()[1], false);
+ if (expr2.is_null())
+ return Longlong_null();
+ interval_type it_type= static_cast<Item_func_timestamp_diff*>(func)->
+ get_int_type();
+ return Longlong_null(expr2.diff(expr1).DDhhmmssff_to_integer_units(it_type));
+ }
+};
+
+
+Longlong_null Item_func_timestamp_diff::datetime_diff()
{
MYSQL_TIME ltime1, ltime2;
ulonglong seconds;
ulong microseconds;
- long months= 0;
int neg= 1;
THD *thd= current_thd;
Datetime::Options opt(TIME_NO_ZEROS, thd);
@@ -3594,7 +3744,7 @@ longlong Item_func_timestamp_diff::val_int()
if (Datetime(thd, args[0], opt).copy_to_mysql_time(&ltime1) ||
Datetime(thd, args[1], opt).copy_to_mysql_time(&ltime2))
- goto null_date;
+ return Longlong_null();
if (calc_time_diff(&ltime2,&ltime1, 1,
&seconds, &microseconds))
@@ -3606,6 +3756,7 @@ longlong Item_func_timestamp_diff::val_int()
{
uint year_beg, year_end, month_beg, month_end, day_beg, day_end;
uint years= 0;
+ long months= 0;
uint second_beg, second_end, microsecond_beg, microsecond_end;
if (neg == -1)
@@ -3653,38 +3804,48 @@ longlong Item_func_timestamp_diff::val_int()
((second_end < second_beg) ||
(second_end == second_beg && microsecond_end < microsecond_beg)))
months-= 1;
+ switch (int_type) {
+ case INTERVAL_YEAR:
+ return Longlong_null(months / 12 * neg);
+ case INTERVAL_QUARTER:
+ return Longlong_null(months / 3 * neg);
+ case INTERVAL_MONTH:
+ return Longlong_null(months * neg);
+ default:
+ break;
+ }
+ DBUG_ASSERT(0);
+ return Longlong_null();
}
- switch (int_type) {
- case INTERVAL_YEAR:
- return months/12*neg;
- case INTERVAL_QUARTER:
- return months/3*neg;
- case INTERVAL_MONTH:
- return months*neg;
- case INTERVAL_WEEK:
- return ((longlong) (seconds / SECONDS_IN_24H / 7L)) * neg;
- case INTERVAL_DAY:
- return ((longlong) (seconds / SECONDS_IN_24H)) * neg;
- case INTERVAL_HOUR:
- return ((longlong) (seconds / 3600L)) * neg;
- case INTERVAL_MINUTE:
- return ((longlong) (seconds / 60L)) * neg;
- case INTERVAL_SECOND:
- return ((longlong) seconds) * neg;
- case INTERVAL_MICROSECOND:
- /*
- In MySQL difference between any two valid datetime values
- in microseconds fits into longlong.
- */
- return ((longlong) ((ulonglong) seconds * 1000000L + microseconds)) * neg;
- default:
- break;
- }
+ return Longlong_null(Sec6(neg < 0, seconds, microseconds).
+ DDhhmmssff_to_integer_units(int_type));
+}
-null_date:
- null_value=1;
- return 0;
+
+bool Item_func_timestamp_diff::fix_length_and_dec(THD *thd)
+{
+ static Func_handler_datetime_diff fh_datetime_diff;
+ static Func_handler_timestamp_diff fh_timestamp_diff;
+ if (args[0]->field_type() == MYSQL_TYPE_TIMESTAMP &&
+ args[1]->field_type() == MYSQL_TYPE_TIMESTAMP)
+ {
+ switch (int_type) {
+ case INTERVAL_WEEK:
+ case INTERVAL_DAY:
+ case INTERVAL_HOUR:
+ case INTERVAL_MINUTE:
+ case INTERVAL_SECOND:
+ case INTERVAL_MICROSECOND:
+ set_func_handler(&fh_timestamp_diff);
+ break;
+ default:
+ set_func_handler(&fh_datetime_diff);
+ }
+ }
+ else
+ set_func_handler(&fh_datetime_diff);
+ return m_func_handler->fix_length_and_dec(this);
}
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index a5f6d9307c6..7b5c1ae58e8 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -1567,7 +1567,7 @@ public:
};
-class Item_func_timestamp_diff :public Item_longlong_func
+class Item_func_timestamp_diff :public Item_handled_func
{
bool check_arguments() const override
{ return check_argument_types_can_return_date(0, arg_count); }
@@ -1577,19 +1577,14 @@ public:
interval_type get_int_type() const { return int_type; };
public:
Item_func_timestamp_diff(THD *thd, Item *a, Item *b, interval_type type_arg):
- Item_longlong_func(thd, a, b), int_type(type_arg) {}
+ Item_handled_func(thd, a, b), int_type(type_arg) {}
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("timestampdiff") };
return name;
}
- longlong val_int() override;
- bool fix_length_and_dec(THD *thd) override
- {
- decimals=0;
- set_maybe_null();
- return FALSE;
- }
+ Longlong_null datetime_diff();
+ bool fix_length_and_dec(THD *thd) override;
void print(String *str, enum_query_type query_type) override;
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_timestamp_diff>(thd, this); }
@@ -1730,6 +1725,37 @@ public:
};
+class Func_handler_date_add_interval_timestamp:
+ public Item_handled_func::Handler_timestamp,
+ public Func_handler_date_add_interval
+{
+public:
+ bool fix_length_and_dec(Item_handled_func *item) const override
+ {
+ uint dec= MY_MAX(item->arguments()[0]->datetime_precision(current_thd),
+ interval_dec(item->arguments()[1], int_type(item)));
+ item->fix_attributes_datetime(dec);
+ return false;
+ }
+};
+
+
+class Func_handler_date_add_interval_timestamp_is_timestamp_plus_YYYYMM:
+ public Func_handler_date_add_interval_timestamp
+{
+public:
+ bool val_native(THD *thd, Item_handled_func *func, Native *to) const override;
+};
+
+
+class Func_handler_date_add_interval_timestamp_is_timestamp_plus_DDhhmmss:
+ public Func_handler_date_add_interval_timestamp
+{
+public:
+ bool val_native(THD *thd, Item_handled_func *func, Native *to) const override;
+};
+
+
class Func_handler_date_add_interval_datetime_arg0_time:
public Func_handler_date_add_interval_datetime
{
@@ -1859,6 +1885,26 @@ public:
};
+class Func_handler_add_time_timestamp:
+ public Item_handled_func::Handler_timestamp,
+ public Func_handler_sign
+{
+public:
+ Func_handler_add_time_timestamp(int sign)
+ :Func_handler_sign(sign)
+ { }
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ THD *thd= current_thd;
+ uint dec0= item->arguments()[0]->datetime_precision(thd);
+ uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]);
+ item->fix_attributes_datetime(MY_MAX(dec0, dec1));
+ return false;
+ }
+ bool val_native(THD *thd, Item_handled_func *func, Native *to) const override;
+};
+
+
class Func_handler_add_time_time:
public Item_handled_func::Handler_time,
public Func_handler_sign
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index 6899654c28f..3119a010794 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -24,8 +24,6 @@
#include <m_ctype.h>
-#define MAX_DAY_NUMBER 3652424L
-
/* Some functions to calculate dates */
/*
@@ -236,7 +234,7 @@ bool get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
uchar *month_pos;
DBUG_ENTER("get_date_from_daynr");
- if (daynr < 366 || daynr > MAX_DAY_NUMBER)
+ if (daynr < 366 || daynr > Date::MAX_DAY_NUMBER())
DBUG_RETURN(1);
year= (uint) (daynr*100 / 36525L);
@@ -962,10 +960,7 @@ bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type,
my_bool neg= 0;
enum enum_mysql_timestamp_type time_type= ltime->time_type;
- if (((ulonglong) interval.day +
- (ulonglong) interval.hour / 24 +
- (ulonglong) interval.minute / 24 / 60 +
- (ulonglong) interval.second / 24 / 60 / 60) > MAX_DAY_NUMBER)
+ if (interval.DDhhmmss_to_days_abs() > Date::MAX_DAY_NUMBER())
goto invalid_date;
if (time_type != MYSQL_TIMESTAMP_TIME)
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index 5d109309d85..6a6cb853a7c 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -401,6 +401,57 @@ bool Timestamp::to_TIME(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) const
}
+
+bool Timestamp::check_zero_timestamp_result_with_warn(THD *thd) const
+{
+ if (!tv_sec && !tv_usec)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_DATETIME_FUNCTION_OVERFLOW,
+ ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW),
+ "timestamp");
+ return true;
+ }
+ return false;
+}
+
+
+bool Sec6::check_timestamp_range_with_warn(THD *thd) const
+{
+ if (neg() || sec() > TIMESTAMP_MAX_VALUE)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_DATETIME_FUNCTION_OVERFLOW,
+ ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW),
+ "timestamp");
+ return true;
+ }
+ return false;
+}
+
+
+bool Timestamp::add(THD *thd, interval_type int_type, const INTERVAL &interval)
+{
+ MYSQL_TIME mysql_time;
+ my_tz_OFFSET0->gmt_sec_to_TIME(&mysql_time, tv_sec);
+ mysql_time.second_part= tv_usec;
+ if (date_add_interval(thd, &mysql_time, int_type, interval))
+ return true;
+ uint error_code;
+ tv_sec= my_tz_OFFSET0->TIME_to_gmt_sec(&mysql_time, &error_code);
+ tv_usec= mysql_time.second_part;
+ if (error_code)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_DATETIME_FUNCTION_OVERFLOW,
+ ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW),
+ "timestamp");
+ return true;
+ }
+ return false;
+};
+
+
Timestamp::Timestamp(THD *thd, const MYSQL_TIME *ltime, uint *error_code)
:Timeval(TIME_to_timestamp(thd, ltime, error_code), ltime->second_part)
{ }
@@ -422,6 +473,22 @@ Timestamp_or_zero_datetime::Timestamp_or_zero_datetime(THD *thd,
}
+bool Timestamp_or_zero_datetime_native::
+ check_zero_datetime_with_warn(THD *thd) const
+{
+ if (is_zero_datetime())
+ {
+ char buff[]= "0000-00-00 00:00:00";
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE,
+ ER_THD(thd, ER_WRONG_VALUE),
+ "timestamp", buff);
+ return true;
+ }
+ return false;
+}
+
+
bool Timestamp_or_zero_datetime::to_TIME(THD *thd, MYSQL_TIME *to,
date_mode_t fuzzydate) const
{
@@ -461,6 +528,19 @@ int Timestamp_or_zero_datetime_native::save_in_field(Field *field,
}
+Timestamp_null::Timestamp_null(THD *thd, Item *item, bool allow_zero_timestamp)
+ :Timestamp(0, 0), Null_flag(true)
+{
+ Timestamp_or_zero_datetime_native_null native(thd, item, false);
+ if (!native.is_null() &&
+ (allow_zero_timestamp || !native.check_zero_datetime_with_warn(thd)))
+ {
+ Timestamp::operator=(Timestamp(native));
+ m_is_null= false;
+ }
+}
+
+
void Sec6::make_from_decimal(const my_decimal *d, ulong *nanoseconds)
{
m_neg= my_decimal2seconds(d, &m_sec, &m_usec, nanoseconds);
diff --git a/sql/sql_type.h b/sql/sql_type.h
index dcdf6438fd9..7c9f6df6126 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -546,10 +546,65 @@ public:
{
make_from_int(Longlong_hybrid(nr, unsigned_val));
}
+ explicit Sec6(bool neg, ulonglong sec, ulong usec)
+ :m_sec(sec), m_usec(usec), m_neg(neg), m_truncated(false)
+ { }
+ explicit Sec6(bool neg, const struct timeval tv)
+ :m_sec(tv.tv_sec), m_usec(tv.tv_usec), m_neg(neg), m_truncated(false)
+ { }
+ explicit Sec6(const struct timeval tv)
+ :m_sec(tv.tv_sec), m_usec(tv.tv_usec), m_neg(false), m_truncated(false)
+ { }
bool neg() const { return m_neg; }
bool truncated() const { return m_truncated; }
ulonglong sec() const { return m_sec; }
long usec() const { return m_usec; }
+ ulonglong to_sec_usec_abs() const
+ {
+ return m_sec * 1000000ULL + m_usec;
+ }
+ longlong to_sec_usec() const
+ {
+ return (longlong) to_sec_usec_abs() * (m_neg ? -1LL : +1LL);
+ }
+
+ Sec6 operator-() const
+ {
+ DBUG_ASSERT(!m_truncated);
+ Sec6 res(*this);
+ res.m_neg= !res.m_neg;
+ return res;
+ }
+
+ Sec6 operator+(const Sec6 &rhs) const
+ {
+ if (m_neg == rhs.m_neg)
+ {
+ DBUG_ASSERT(!m_truncated);
+ DBUG_ASSERT(!rhs.m_truncated);
+ Sec6 res(*this);
+ res.m_sec+= rhs.m_sec;
+ res.m_usec+= rhs.m_usec;
+ if (res.m_usec > 1000000)
+ {
+ res.m_sec++;
+ res.m_usec-= 1000000;
+ }
+ return res;
+ }
+ longlong sec_usec= to_sec_usec() + rhs.to_sec_usec();
+ bool neg= sec_usec < 0;
+ if (neg)
+ sec_usec*= -1;
+ return Sec6(neg, (ulonglong) sec_usec / 1000000ULL,
+ (ulonglong) sec_usec % 1000000ULL);
+ }
+
+ Sec6 operator-(const Sec6 &rhs) const
+ {
+ return *this + (-rhs);
+ }
+
/**
Converts Sec6 to MYSQL_TIME
@param thd current thd
@@ -650,6 +705,44 @@ public:
ltime->second_part= m_usec;
return false;
}
+
+ /*
+ Convert to MariaDB interval units.
+ Only the whole part of the specified unit is returned (without fractions).
+ E.g. in case of INTERVAL_SECOND microseconds are not mixed to the result.
+ */
+ ulonglong DDhhmmssff_to_integer_units_abs(interval_type int_type) const
+ {
+ switch (int_type) {
+ case INTERVAL_WEEK:
+ return m_sec / SECONDS_IN_24H / 7ULL;
+ case INTERVAL_DAY:
+ return m_sec / SECONDS_IN_24H;
+ case INTERVAL_HOUR:
+ return m_sec / 3600ULL;
+ case INTERVAL_MINUTE:
+ return m_sec / 60ULL;
+ case INTERVAL_SECOND:
+ return m_sec;
+ case INTERVAL_MICROSECOND:
+ /*
+ In MySQL difference between any two valid datetime values
+ in microseconds fits into longlong.
+ */
+ return to_sec_usec_abs();
+ default:
+ break;
+ }
+ DBUG_ASSERT(0);
+ return 0;
+ }
+
+ longlong DDhhmmssff_to_integer_units(interval_type int_type) const
+ {
+ return (longlong) DDhhmmssff_to_integer_units_abs(int_type) *
+ (m_neg ? -1LL : +1LL);
+ }
+
Sec6 &trunc(uint dec)
{
m_usec-= my_time_fraction_remainder(m_usec, dec);
@@ -663,6 +756,7 @@ public:
my_snprintf(to, nbytes, "%s%llu", m_neg ? "-" : "", m_sec);
}
void make_truncated_warning(THD *thd, const char *type_str) const;
+ bool check_timestamp_range_with_warn(THD *thd) const;
};
@@ -1492,6 +1586,17 @@ public:
dec));
return str;
}
+ ulonglong to_seconds_abs() const
+ {
+ return (ulonglong) MYSQL_TIME::hour * 60 * 60 +
+ (ulonglong) MYSQL_TIME::minute * 60 +
+ (ulonglong) MYSQL_TIME::second;
+ }
+ Sec6 to_sec6() const
+ {
+ DBUG_ASSERT(is_valid_interval_DDhhmmssff());
+ return Sec6(MYSQL_TIME::neg, to_seconds_abs(), MYSQL_TIME::second_part);
+ }
};
class Schema;
@@ -2157,6 +2262,11 @@ public:
:Temporal_with_date::Options(fuzzydate)
{ }
};
+ // Day number for '9999-12-31'
+ static constexpr uint MAX_DAY_NUMBER()
+ {
+ return 3652424;
+ }
public:
Date(Item *item, date_mode_t fuzzydate)
:Date(current_thd, item, fuzzydate)
@@ -2765,6 +2875,46 @@ public:
int warn= 0;
return round(dec, mode, &warn);
}
+ bool check_zero_timestamp_result_with_warn(THD *thd) const;
+ /*
+ Add an arbitrary interval.
+ Uses MYSQL_TIME representation internally.
+ */
+ bool add(THD *thd, interval_type int_type, const INTERVAL &interval);
+
+ /*
+ Add or subtract a simple interval represented as Timeval.
+ */
+ bool add(THD *thd, const Sec6 &rhs)
+ {
+ Sec6 res= Sec6(*this) + rhs;
+ if (res.check_timestamp_range_with_warn(thd))
+ return true;
+ *this= Timestamp(res.sec(), res.usec());
+ return false;
+ }
+ bool sub(THD *thd, const Sec6 &rhs)
+ {
+ Sec6 res= Sec6(*this) - rhs;
+ if (res.check_timestamp_range_with_warn(thd))
+ return true;
+ *this= Timestamp(res.sec(), res.usec());
+ return false;
+ }
+
+ Sec6 diff(const Timestamp &rhs) const
+ {
+ return Sec6(tv()) - Sec6(rhs.tv());
+ }
+
+};
+
+
+class Timestamp_null: public Timestamp, public Null_flag
+{
+public:
+ // TODO: change this to pass NO_ZERO_DATE instead of bool?
+ explicit Timestamp_null(THD *thd, Item *item, bool allow_zero_timestamp);
};
@@ -2846,6 +2996,7 @@ public:
{
return length() == 0;
}
+ bool check_zero_datetime_with_warn(THD *thd) const;
};
diff --git a/sql/structs.h b/sql/structs.h
index b36f8e6a1a0..e60dc83650a 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -204,6 +204,29 @@ typedef struct {
ulong year,month,day,hour;
ulonglong minute,second,second_part;
bool neg;
+ ulonglong DDhhmmss_to_integer_seconds_abs() const
+ {
+ return second +
+ (minute * 60) +
+ (hour * 60 * 60) +
+ ((ulonglong) day * 24 * 60 * 60);
+ }
+ ulonglong DDhhmmssff_to_integer_seconds_abs() const
+ {
+ return DDhhmmss_to_integer_seconds_abs() + second_part / 1000000;
+ }
+ ulonglong DDhhmmss_to_days_abs() const
+ {
+ return (ulonglong) day +
+ hour / 24 +
+ minute / 24 / 60 +
+ second / 24 / 60 / 60;
+ }
+ ulonglong DDhhmmssff_to_days_abs() const
+ {
+ return DDhhmmss_to_days_abs() +
+ second_part / 24 / 60 / 60 / 1000000;
+ }
} INTERVAL;