mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +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);
|
||||
set timestamp=1288477526;
|
||||
insert t1 values (null);
|
||||
insert t1 values ();
|
||||
set timestamp=1288481126;
|
||||
insert t1 values (null);
|
||||
insert t1 values ();
|
||||
select a, unix_timestamp(a) from t1;
|
||||
a unix_timestamp(a)
|
||||
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
|
||||
set global mysql56_temporal_format=true;
|
||||
select a, unix_timestamp(a) from t1;
|
||||
a unix_timestamp(a)
|
||||
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
|
||||
alter table t1 modify a timestamp;
|
||||
select a, unix_timestamp(a) from t1;
|
||||
a unix_timestamp(a)
|
||||
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
|
||||
drop table t1;
|
||||
set global mysql56_temporal_format=false;
|
||||
|
@ -93,8 +93,10 @@ set global mysql56_temporal_format=false;
|
||||
create table t1 (a timestamp);
|
||||
set timestamp=1288477526;
|
||||
insert t1 values (null);
|
||||
insert t1 values ();
|
||||
set timestamp=1288481126;
|
||||
insert t1 values (null);
|
||||
insert t1 values ();
|
||||
select a, unix_timestamp(a) from t1;
|
||||
set global mysql56_temporal_format=true;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
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
|
||||
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);
|
||||
}
|
||||
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
|
||||
@ -2403,7 +2403,6 @@ public:
|
||||
void sql_type(String &str) const;
|
||||
bool zero_pack() const { return 0; }
|
||||
int set_time();
|
||||
bool set_explicit_default(Item *value);
|
||||
int evaluate_update_default_function()
|
||||
{
|
||||
int res= 0;
|
||||
|
@ -1734,6 +1734,25 @@ void Item_func_now::print(String *str, enum_query_type query_type)
|
||||
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
|
||||
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:
|
||||
Item_func_now_local(THD *thd, uint dec): Item_func_now(thd, dec) {}
|
||||
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 enum Functype functype() const { return NOW_FUNC; }
|
||||
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
|
||||
|
Reference in New Issue
Block a user