Commit bc4409ab authored by Sergei Golubchik's avatar Sergei Golubchik

MDEV-13868 cannot insert 1288481126 in a timestamp column in Europe/Moscow

make insert NULL into a timestamp mark the field as having an
explicit value. So that the field won't be assigned the value
again in TABLE::update_default_field()

make Item_func_now_local::save_in_field(timestamp_field) not to go
through MYSQL_TIME - this conversion is lossy around DST change times.
This fixes inserting a default value into a timestamp field.
parent f1ce69f3
...@@ -132,21 +132,29 @@ set global mysql56_temporal_format=false; ...@@ -132,21 +132,29 @@ set global mysql56_temporal_format=false;
create table t1 (a timestamp); create table t1 (a timestamp);
set timestamp=1288477526; set timestamp=1288477526;
insert t1 values (null); insert t1 values (null);
insert t1 values ();
set timestamp=1288481126; set timestamp=1288481126;
insert t1 values (null); insert t1 values (null);
insert t1 values ();
select a, unix_timestamp(a) from t1; select a, unix_timestamp(a) from t1;
a unix_timestamp(a) a unix_timestamp(a)
2010-10-31 02:25:26 1288477526 2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288481126
2010-10-31 02:25:26 1288481126 2010-10-31 02:25:26 1288481126
set global mysql56_temporal_format=true; set global mysql56_temporal_format=true;
select a, unix_timestamp(a) from t1; select a, unix_timestamp(a) from t1;
a unix_timestamp(a) a unix_timestamp(a)
2010-10-31 02:25:26 1288477526 2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288481126
2010-10-31 02:25:26 1288481126 2010-10-31 02:25:26 1288481126
alter table t1 modify a timestamp; alter table t1 modify a timestamp;
select a, unix_timestamp(a) from t1; select a, unix_timestamp(a) from t1;
a unix_timestamp(a) a unix_timestamp(a)
2010-10-31 02:25:26 1288477526 2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288481126
2010-10-31 02:25:26 1288481126 2010-10-31 02:25:26 1288481126
drop table t1; drop table t1;
set global mysql56_temporal_format=false; set global mysql56_temporal_format=false;
......
...@@ -93,8 +93,10 @@ set global mysql56_temporal_format=false; ...@@ -93,8 +93,10 @@ set global mysql56_temporal_format=false;
create table t1 (a timestamp); create table t1 (a timestamp);
set timestamp=1288477526; set timestamp=1288477526;
insert t1 values (null); insert t1 values (null);
insert t1 values ();
set timestamp=1288481126; set timestamp=1288481126;
insert t1 values (null); insert t1 values (null);
insert t1 values ();
select a, unix_timestamp(a) from t1; select a, unix_timestamp(a) from t1;
set global mysql56_temporal_format=true; set global mysql56_temporal_format=true;
select a, unix_timestamp(a) from t1; select a, unix_timestamp(a) from t1;
......
...@@ -5260,36 +5260,6 @@ int Field_timestamp::set_time() ...@@ -5260,36 +5260,6 @@ int Field_timestamp::set_time()
return 0; return 0;
} }
/**
Mark the field as having an explicit default value.
@param value if available, the value that the field is being set to
@returns whether the explicit default bit was set
@note
Fields that have an explicit default value should not be updated
automatically via the DEFAULT or ON UPDATE functions. The functions
that deal with data change functionality (INSERT/UPDATE/LOAD),
determine if there is an explicit value for each field before performing
the data change, and call this method to mark the field.
For timestamp columns, the only case where a column is not marked
as been given a value are:
- It's explicitly assigned with DEFAULT
- We assign NULL to a timestamp field that is defined as NOT NULL.
This is how MySQL has worked since it's start.
*/
bool Field_timestamp::set_explicit_default(Item *value)
{
if (((value->type() == Item::DEFAULT_VALUE_ITEM &&
!((Item_default_value*)value)->arg) ||
(!maybe_null() && value->null_value)))
return false;
set_has_explicit_value();
return true;
}
#ifdef NOT_USED #ifdef NOT_USED
static void store_native(ulonglong num, uchar *to, uint bytes) static void store_native(ulonglong num, uchar *to, uint bytes)
{ {
......
...@@ -975,7 +975,7 @@ class Field: public Value_source ...@@ -975,7 +975,7 @@ class Field: public Value_source
{ {
return bitmap_is_set(&table->has_value_set, field_index); return bitmap_is_set(&table->has_value_set, field_index);
} }
virtual bool set_explicit_default(Item *value); bool set_explicit_default(Item *value);
/** /**
Evaluates the @c UPDATE default function, if one exists, and stores the Evaluates the @c UPDATE default function, if one exists, and stores the
...@@ -2403,7 +2403,6 @@ class Field_timestamp :public Field_temporal { ...@@ -2403,7 +2403,6 @@ class Field_timestamp :public Field_temporal {
void sql_type(String &str) const; void sql_type(String &str) const;
bool zero_pack() const { return 0; } bool zero_pack() const { return 0; }
int set_time(); int set_time();
bool set_explicit_default(Item *value);
int evaluate_update_default_function() int evaluate_update_default_function()
{ {
int res= 0; int res= 0;
......
...@@ -1734,6 +1734,25 @@ void Item_func_now::print(String *str, enum_query_type query_type) ...@@ -1734,6 +1734,25 @@ void Item_func_now::print(String *str, enum_query_type query_type)
str->append(')'); str->append(')');
} }
int Item_func_now_local::save_in_field(Field *field, bool no_conversions)
{
if (field->type() == MYSQL_TYPE_TIMESTAMP)
{
THD *thd= field->get_thd();
my_time_t ts= thd->query_start();
uint dec= MY_MIN(decimals, field->decimals());
ulong sec_part= dec ? thd->query_start_sec_part() : 0;
sec_part-= my_time_fraction_remainder(sec_part, dec);
field->set_notnull();
((Field_timestamp*)field)->store_TIME(ts, sec_part);
return 0;
}
else
return Item_temporal_func::save_in_field(field, no_conversions);
}
/** /**
Converts current time in my_time_t to MYSQL_TIME represenatation for local Converts current time in my_time_t to MYSQL_TIME represenatation for local
time zone. Defines time zone (local) used for whole NOW function. time zone. Defines time zone (local) used for whole NOW function.
......
...@@ -748,6 +748,7 @@ class Item_func_now_local :public Item_func_now ...@@ -748,6 +748,7 @@ class Item_func_now_local :public Item_func_now
public: public:
Item_func_now_local(THD *thd, uint dec): Item_func_now(thd, dec) {} Item_func_now_local(THD *thd, uint dec): Item_func_now(thd, dec) {}
const char *func_name() const { return "current_timestamp"; } const char *func_name() const { return "current_timestamp"; }
int save_in_field(Field *field, bool no_conversions);
virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time); virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time);
virtual enum Functype functype() const { return NOW_FUNC; } virtual enum Functype functype() const { return NOW_FUNC; }
Item *get_copy(THD *thd, MEM_ROOT *mem_root) Item *get_copy(THD *thd, MEM_ROOT *mem_root)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment