mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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.
This commit is contained in:
@ -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;
|
|||||||
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;
|
||||||
|
30
sql/field.cc
30
sql/field.cc
@ -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 @@ public:
|
|||||||
{
|
{
|
||||||
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 @@ public:
|
|||||||
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)
|
|||||||
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
|
|||||||
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)
|
||||||
|
Reference in New Issue
Block a user