mirror of
https://github.com/MariaDB/server.git
synced 2025-08-07 00:04:31 +03:00
Fix for bug #4131 "TIMESTAMP columns missing minutes and seconds when
using GROUP BY" Now we are setting Field_timestamp::field_length to 19 in open_table() if we are in new mode (and we are restoring it back when we are coming back to normal mode). This also should solve potential problems with some of LOAD DATA INFILE and SELECT * INTO in this mode.
This commit is contained in:
@@ -122,40 +122,41 @@ t2 t4 t6 t8 t10 t12 t14
|
|||||||
0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00
|
0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00
|
||||||
1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59
|
1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
set new=0;
|
||||||
create table t1 (t1 timestamp default '2003-01-01 00:00:00',
|
create table t1 (t1 timestamp default '2003-01-01 00:00:00',
|
||||||
t2 timestamp default '2003-01-01 00:00:00');
|
t2 timestamp default '2003-01-01 00:00:00');
|
||||||
set TIMESTAMP=1000000000;
|
set TIMESTAMP=1000000000;
|
||||||
insert into t1 values();
|
insert into t1 values();
|
||||||
select * from t1;
|
select * from t1;
|
||||||
t1 t2
|
t1 t2
|
||||||
2001-09-09 04:46:40 2003-01-01 00:00:00
|
20010909044640 20030101000000
|
||||||
show create table t1;
|
show create table t1;
|
||||||
Table Create Table
|
Table Create Table
|
||||||
t1 CREATE TABLE `t1` (
|
t1 CREATE TABLE `t1` (
|
||||||
`t1` timestamp(14) NOT NULL,
|
`t1` timestamp(14) NOT NULL,
|
||||||
`t2` timestamp(14) NOT NULL default '2003-01-01 00:00:00'
|
`t2` timestamp(14) NOT NULL default '20030101000000'
|
||||||
) TYPE=MyISAM
|
) TYPE=MyISAM
|
||||||
show columns from t1;
|
show columns from t1;
|
||||||
Field Type Null Key Default Extra
|
Field Type Null Key Default Extra
|
||||||
t1 timestamp(14) YES NULL
|
t1 timestamp(14) YES NULL
|
||||||
t2 timestamp(14) YES 2003-01-01 00:00:00
|
t2 timestamp(14) YES 20030101000000
|
||||||
show columns from t1 like 't2';
|
show columns from t1 like 't2';
|
||||||
Field Type Null Key Default Extra
|
Field Type Null Key Default Extra
|
||||||
t2 timestamp(14) YES 2003-01-01 00:00:00
|
t2 timestamp(14) YES 20030101000000
|
||||||
create table t2 (select * from t1);
|
create table t2 (select * from t1);
|
||||||
show create table t2;
|
show create table t2;
|
||||||
Table Create Table
|
Table Create Table
|
||||||
t2 CREATE TABLE `t2` (
|
t2 CREATE TABLE `t2` (
|
||||||
`t1` timestamp(14) NOT NULL,
|
`t1` timestamp(14) NOT NULL,
|
||||||
`t2` timestamp(14) NOT NULL default '2003-01-01 00:00:00'
|
`t2` timestamp(14) NOT NULL default '20030101000000'
|
||||||
) TYPE=MyISAM
|
) TYPE=MyISAM
|
||||||
alter table t1 add column t0 timestamp first;
|
alter table t1 add column t0 timestamp first;
|
||||||
show create table t1;
|
show create table t1;
|
||||||
Table Create Table
|
Table Create Table
|
||||||
t1 CREATE TABLE `t1` (
|
t1 CREATE TABLE `t1` (
|
||||||
`t0` timestamp(14) NOT NULL,
|
`t0` timestamp(14) NOT NULL,
|
||||||
`t1` timestamp(14) NOT NULL default '2003-01-01 00:00:00',
|
`t1` timestamp(14) NOT NULL default '20030101000000',
|
||||||
`t2` timestamp(14) NOT NULL default '2003-01-01 00:00:00'
|
`t2` timestamp(14) NOT NULL default '20030101000000'
|
||||||
) TYPE=MyISAM
|
) TYPE=MyISAM
|
||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
create table t1 (ts1 timestamp, ts2 timestamp);
|
create table t1 (ts1 timestamp, ts2 timestamp);
|
||||||
@@ -164,8 +165,8 @@ insert into t1 values ();
|
|||||||
insert into t1 values (DEFAULT, DEFAULT);
|
insert into t1 values (DEFAULT, DEFAULT);
|
||||||
select * from t1;
|
select * from t1;
|
||||||
ts1 ts2
|
ts1 ts2
|
||||||
2001-09-09 04:46:40 0000-00-00 00:00:00
|
20010909044640 00000000000000
|
||||||
2001-09-09 04:46:40 0000-00-00 00:00:00
|
20010909044640 00000000000000
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (ts timestamp(19));
|
create table t1 (ts timestamp(19));
|
||||||
show create table t1;
|
show create table t1;
|
||||||
@@ -179,3 +180,44 @@ select * from t1;
|
|||||||
ts
|
ts
|
||||||
2001-09-09 04:46:40
|
2001-09-09 04:46:40
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
set new=1;
|
||||||
|
create table t1 (a char(2), t timestamp);
|
||||||
|
insert into t1 values ('a', '2004-01-01 00:00:00'), ('a', '2004-01-01 01:00:00'),
|
||||||
|
('b', '2004-02-01 00:00:00');
|
||||||
|
select max(t) from t1 group by a;
|
||||||
|
max(t)
|
||||||
|
2004-01-01 01:00:00
|
||||||
|
2004-02-01 00:00:00
|
||||||
|
drop table t1;
|
||||||
|
create table t1 (ts1 timestamp);
|
||||||
|
show create table t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`ts1` timestamp(19) NOT NULL
|
||||||
|
) TYPE=MyISAM
|
||||||
|
alter table t1 add ts2 timestamp;
|
||||||
|
set new=0;
|
||||||
|
show create table t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`ts1` timestamp(19) NOT NULL,
|
||||||
|
`ts2` timestamp(19) NOT NULL default '0000-00-00 00:00:00'
|
||||||
|
) TYPE=MyISAM
|
||||||
|
drop table t1;
|
||||||
|
create table t1 (ts1 timestamp);
|
||||||
|
insert into t1 values ('2004-01-01 00:00:00'), ('2004-01-01 01:00:00');
|
||||||
|
select * from t1;
|
||||||
|
ts1
|
||||||
|
20040101000000
|
||||||
|
20040101010000
|
||||||
|
set new=1;
|
||||||
|
select * from t1;
|
||||||
|
ts1
|
||||||
|
2004-01-01 00:00:00
|
||||||
|
2004-01-01 01:00:00
|
||||||
|
set new=0;
|
||||||
|
select * from t1;
|
||||||
|
ts1
|
||||||
|
20040101000000
|
||||||
|
20040101010000
|
||||||
|
drop table t1;
|
||||||
|
@@ -71,6 +71,7 @@ select * from t1;
|
|||||||
set new=1;
|
set new=1;
|
||||||
select * from t1;
|
select * from t1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
set new=0;
|
||||||
|
|
||||||
#
|
#
|
||||||
# Bug #1885, bug #2539.
|
# Bug #1885, bug #2539.
|
||||||
@@ -116,3 +117,34 @@ set TIMESTAMP=1000000000;
|
|||||||
insert into t1 values ();
|
insert into t1 values ();
|
||||||
select * from t1;
|
select * from t1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test for bug #4131, TIMESTAMP columns missing minutes and seconds when
|
||||||
|
# using GROUP BY in @@new=1 mode.
|
||||||
|
#
|
||||||
|
set new=1;
|
||||||
|
create table t1 (a char(2), t timestamp);
|
||||||
|
insert into t1 values ('a', '2004-01-01 00:00:00'), ('a', '2004-01-01 01:00:00'),
|
||||||
|
('b', '2004-02-01 00:00:00');
|
||||||
|
select max(t) from t1 group by a;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# More --new mode tests
|
||||||
|
# Both columns created before and during alter should have same length.
|
||||||
|
#
|
||||||
|
create table t1 (ts1 timestamp);
|
||||||
|
show create table t1;
|
||||||
|
alter table t1 add ts2 timestamp;
|
||||||
|
set new=0;
|
||||||
|
show create table t1;
|
||||||
|
drop table t1;
|
||||||
|
# Selecting from table in --new mode should not affect further selects.
|
||||||
|
create table t1 (ts1 timestamp);
|
||||||
|
insert into t1 values ('2004-01-01 00:00:00'), ('2004-01-01 01:00:00');
|
||||||
|
select * from t1;
|
||||||
|
set new=1;
|
||||||
|
select * from t1;
|
||||||
|
set new=0;
|
||||||
|
select * from t1;
|
||||||
|
drop table t1;
|
||||||
|
24
sql/field.cc
24
sql/field.cc
@@ -2467,8 +2467,7 @@ void Field_double::sql_type(String &res) const
|
|||||||
|
|
||||||
enum Item_result Field_timestamp::result_type() const
|
enum Item_result Field_timestamp::result_type() const
|
||||||
{
|
{
|
||||||
return (!current_thd->variables.new_mode &&
|
return ((field_length == 8 || field_length == 14) ? INT_RESULT :
|
||||||
(field_length == 8 || field_length == 14) ? INT_RESULT :
|
|
||||||
STRING_RESULT);
|
STRING_RESULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2480,6 +2479,9 @@ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg,
|
|||||||
:Field_num(ptr_arg, len_arg, (uchar*) 0,0,
|
:Field_num(ptr_arg, len_arg, (uchar*) 0,0,
|
||||||
unireg_check_arg, field_name_arg, table_arg,
|
unireg_check_arg, field_name_arg, table_arg,
|
||||||
0, 1, 1)
|
0, 1, 1)
|
||||||
|
#if MYSQL_VERSION_ID < 40100
|
||||||
|
, orig_field_length(len_arg)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if (table && !table->timestamp_field)
|
if (table && !table->timestamp_field)
|
||||||
{
|
{
|
||||||
@@ -2697,7 +2699,7 @@ String *Field_timestamp::val_str(String *val_buffer,
|
|||||||
time_t time_arg;
|
time_t time_arg;
|
||||||
struct tm *l_time;
|
struct tm *l_time;
|
||||||
struct tm tm_tmp;
|
struct tm tm_tmp;
|
||||||
my_bool new_format= (current_thd->variables.new_mode) || field_length == 19,
|
my_bool new_format= field_length == 19,
|
||||||
full_year=(field_length == 8 || field_length == 14 || new_format);
|
full_year=(field_length == 8 || field_length == 14 || new_format);
|
||||||
int real_field_length= new_format ? 19 : field_length;
|
int real_field_length= new_format ? 19 : field_length;
|
||||||
|
|
||||||
@@ -2859,22 +2861,6 @@ void Field_timestamp::set_time()
|
|||||||
longstore(ptr,tmp);
|
longstore(ptr,tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
This is an exact copy of Field_num except that 'length' is depending
|
|
||||||
on --new mode
|
|
||||||
*/
|
|
||||||
|
|
||||||
void Field_timestamp::make_field(Send_field *field)
|
|
||||||
{
|
|
||||||
field->table_name=table_name;
|
|
||||||
field->col_name=field_name;
|
|
||||||
/* If --new, then we are using "YYYY-MM-DD HH:MM:SS" format */
|
|
||||||
field->length= current_thd->variables.new_mode ? 19 : field_length;
|
|
||||||
field->type=type();
|
|
||||||
field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
|
|
||||||
field->decimals=dec;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
** time type
|
** time type
|
||||||
|
13
sql/field.h
13
sql/field.h
@@ -546,6 +546,13 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
class Field_timestamp :public Field_num {
|
class Field_timestamp :public Field_num {
|
||||||
|
#if MYSQL_VERSION_ID < 40100
|
||||||
|
/*
|
||||||
|
We save the original field length here because field_length is
|
||||||
|
changed to a mock value in case when the 'new_mode' is in effect.
|
||||||
|
*/
|
||||||
|
uint32 orig_field_length;
|
||||||
|
#endif
|
||||||
public:
|
public:
|
||||||
Field_timestamp(char *ptr_arg, uint32 len_arg,
|
Field_timestamp(char *ptr_arg, uint32 len_arg,
|
||||||
enum utype unireg_check_arg, const char *field_name_arg,
|
enum utype unireg_check_arg, const char *field_name_arg,
|
||||||
@@ -587,7 +594,11 @@ public:
|
|||||||
void fill_and_store(char *from,uint len);
|
void fill_and_store(char *from,uint len);
|
||||||
bool get_date(TIME *ltime,bool fuzzydate);
|
bool get_date(TIME *ltime,bool fuzzydate);
|
||||||
bool get_time(TIME *ltime);
|
bool get_time(TIME *ltime);
|
||||||
void make_field(Send_field *field);
|
|
||||||
|
#if MYSQL_VERSION_ID < 40100
|
||||||
|
friend TABLE *open_table(THD *thd,const char *db,const char *table_name,
|
||||||
|
const char *alias,bool *refresh);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@@ -941,6 +941,31 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
|
|||||||
for (uint i=0 ; i < table->fields ; i++)
|
for (uint i=0 ; i < table->fields ; i++)
|
||||||
table->field[i]->table_name=table->table_name;
|
table->field[i]->table_name=table->table_name;
|
||||||
}
|
}
|
||||||
|
#if MYSQL_VERSION_ID < 40100
|
||||||
|
/*
|
||||||
|
If per-connection "new" variable (represented by variables.new_mode)
|
||||||
|
is set then we should pretend that the length of TIMESTAMP field is 19.
|
||||||
|
The cheapest (from perfomance viewpoint) way to achieve that is to set
|
||||||
|
field_length of all Field_timestamp objects in a table after opening
|
||||||
|
it (to 19 if new_mode is true or to original field length otherwise).
|
||||||
|
We save value of new_mode variable in TABLE::timestamp_mode to
|
||||||
|
not perform this setup if new_mode value is the same between sequential
|
||||||
|
table opens.
|
||||||
|
*/
|
||||||
|
my_bool new_mode= thd->variables.new_mode;
|
||||||
|
if (table->timestamp_mode != new_mode)
|
||||||
|
{
|
||||||
|
for (uint i=0 ; i < table->fields ; i++)
|
||||||
|
{
|
||||||
|
Field *field= table->field[i];
|
||||||
|
|
||||||
|
if (field->type() == FIELD_TYPE_TIMESTAMP)
|
||||||
|
field->field_length= new_mode ? 19 :
|
||||||
|
((Field_timestamp *)(field))->orig_field_length;
|
||||||
|
}
|
||||||
|
table->timestamp_mode= new_mode;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
/* These variables are also set in reopen_table() */
|
/* These variables are also set in reopen_table() */
|
||||||
table->tablenr=thd->current_tablenr++;
|
table->tablenr=thd->current_tablenr++;
|
||||||
table->used_fields=0;
|
table->used_fields=0;
|
||||||
|
@@ -3229,7 +3229,18 @@ bool add_field_to_list(char *field_name, enum_field_types type,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FIELD_TYPE_TIMESTAMP:
|
case FIELD_TYPE_TIMESTAMP:
|
||||||
|
#if MYSQL_VERSION_ID < 40100
|
||||||
|
/*
|
||||||
|
When in in --new mode, we should create TIMESTAMP(19) fields by default;
|
||||||
|
otherwise we will have problems with ALTER TABLE changing lengths of
|
||||||
|
existing TIMESTAMP fields to 19 and adding new fields with length 14.
|
||||||
|
*/
|
||||||
|
if (thd->variables.new_mode)
|
||||||
|
new_field->length= 19;
|
||||||
|
else if (!length)
|
||||||
|
#else
|
||||||
if (!length)
|
if (!length)
|
||||||
|
#endif
|
||||||
new_field->length= 14; // Full date YYYYMMDDHHMMSS
|
new_field->length= 14; // Full date YYYYMMDDHHMMSS
|
||||||
else if (new_field->length != 19)
|
else if (new_field->length != 19)
|
||||||
{
|
{
|
||||||
|
@@ -106,6 +106,14 @@ struct st_table {
|
|||||||
*found_next_number_field, /* Set on open */
|
*found_next_number_field, /* Set on open */
|
||||||
*rowid_field;
|
*rowid_field;
|
||||||
Field_timestamp *timestamp_field;
|
Field_timestamp *timestamp_field;
|
||||||
|
#if MYSQL_VERSION_ID < 40100
|
||||||
|
/*
|
||||||
|
Indicates whenever we have to set field_length members of all TIMESTAMP
|
||||||
|
fields to 19 (to honour 'new_mode' variable) or to original
|
||||||
|
field_length values.
|
||||||
|
*/
|
||||||
|
my_bool timestamp_mode;
|
||||||
|
#endif
|
||||||
my_string comment; /* Comment about table */
|
my_string comment; /* Comment about table */
|
||||||
REGINFO reginfo; /* field connections */
|
REGINFO reginfo; /* field connections */
|
||||||
MEM_ROOT mem_root;
|
MEM_ROOT mem_root;
|
||||||
|
Reference in New Issue
Block a user