mirror of
https://github.com/MariaDB/server.git
synced 2025-08-05 13:16:09 +03:00
Bug 4937: different date -> string conversion when using
SELECT ... UNION and INSERT ... SELECT ... UNION
This commit is contained in:
@@ -79,3 +79,20 @@ SELECT DATE_FORMAT(f1, "%l.%i %p") , DATE_FORMAT(f2, "%l.%i %p") FROM t1;
|
|||||||
DATE_FORMAT(f1, "%l.%i %p") DATE_FORMAT(f2, "%l.%i %p")
|
DATE_FORMAT(f1, "%l.%i %p") DATE_FORMAT(f2, "%l.%i %p")
|
||||||
9.00 AM 12.00 PM
|
9.00 AM 12.00 PM
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (f1 DATE);
|
||||||
|
CREATE TABLE t2 (f2 VARCHAR(8));
|
||||||
|
CREATE TABLE t3 (f2 CHAR(8));
|
||||||
|
INSERT INTO t1 VALUES ('1978-11-26');
|
||||||
|
INSERT INTO t2 SELECT f1+0 FROM t1;
|
||||||
|
INSERT INTO t2 SELECT f1+0 FROM t1 UNION SELECT f1+0 FROM t1;
|
||||||
|
INSERT INTO t3 SELECT f1+0 FROM t1;
|
||||||
|
INSERT INTO t3 SELECT f1+0 FROM t1 UNION SELECT f1+0 FROM t1;
|
||||||
|
SELECT * FROM t2;
|
||||||
|
f2
|
||||||
|
19781126
|
||||||
|
19781126
|
||||||
|
SELECT * FROM t3;
|
||||||
|
f2
|
||||||
|
19781126
|
||||||
|
19781126
|
||||||
|
DROP TABLE t1, t2, t3;
|
||||||
|
@@ -84,3 +84,22 @@ CREATE TABLE t1 (f1 time default NULL, f2 time default NULL) TYPE=MyISAM;
|
|||||||
INSERT INTO t1 (f1, f2) VALUES ('09:00', '12:00');
|
INSERT INTO t1 (f1, f2) VALUES ('09:00', '12:00');
|
||||||
SELECT DATE_FORMAT(f1, "%l.%i %p") , DATE_FORMAT(f2, "%l.%i %p") FROM t1;
|
SELECT DATE_FORMAT(f1, "%l.%i %p") , DATE_FORMAT(f2, "%l.%i %p") FROM t1;
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bug 4937: different date -> string conversion when using SELECT ... UNION
|
||||||
|
# and INSERT ... SELECT ... UNION
|
||||||
|
#
|
||||||
|
|
||||||
|
CREATE TABLE t1 (f1 DATE);
|
||||||
|
CREATE TABLE t2 (f2 VARCHAR(8));
|
||||||
|
CREATE TABLE t3 (f2 CHAR(8));
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES ('1978-11-26');
|
||||||
|
INSERT INTO t2 SELECT f1+0 FROM t1;
|
||||||
|
INSERT INTO t2 SELECT f1+0 FROM t1 UNION SELECT f1+0 FROM t1;
|
||||||
|
INSERT INTO t3 SELECT f1+0 FROM t1;
|
||||||
|
INSERT INTO t3 SELECT f1+0 FROM t1 UNION SELECT f1+0 FROM t1;
|
||||||
|
SELECT * FROM t2;
|
||||||
|
SELECT * FROM t3;
|
||||||
|
|
||||||
|
DROP TABLE t1, t2, t3;
|
||||||
|
90
sql/field.cc
90
sql/field.cc
@@ -37,6 +37,7 @@
|
|||||||
#include "sql_select.h"
|
#include "sql_select.h"
|
||||||
#include <m_ctype.h>
|
#include <m_ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
#ifdef HAVE_FCONVERT
|
#ifdef HAVE_FCONVERT
|
||||||
#include <floatingpoint.h>
|
#include <floatingpoint.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -58,6 +59,8 @@ template class List_iterator<create_field>;
|
|||||||
uchar Field_null::null[1]={1};
|
uchar Field_null::null[1]={1};
|
||||||
const char field_separator=',';
|
const char field_separator=',';
|
||||||
|
|
||||||
|
#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE 320
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
Static help functions
|
Static help functions
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@@ -739,7 +742,7 @@ void Field_decimal::store(double nr)
|
|||||||
|
|
||||||
reg4 uint i,length;
|
reg4 uint i,length;
|
||||||
char fyllchar,*to;
|
char fyllchar,*to;
|
||||||
char buff[320];
|
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
|
||||||
|
|
||||||
fyllchar = zerofill ? (char) '0' : (char) ' ';
|
fyllchar = zerofill ? (char) '0' : (char) ' ';
|
||||||
#ifdef HAVE_SNPRINTF
|
#ifdef HAVE_SNPRINTF
|
||||||
@@ -2326,46 +2329,20 @@ String *Field_double::val_str(String *val_buffer,
|
|||||||
#endif
|
#endif
|
||||||
doubleget(nr,ptr);
|
doubleget(nr,ptr);
|
||||||
|
|
||||||
uint to_length=max(field_length,320);
|
uint to_length=max(field_length, DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE);
|
||||||
val_buffer->alloc(to_length);
|
val_buffer->alloc(to_length);
|
||||||
char *to=(char*) val_buffer->ptr();
|
char *to=(char*) val_buffer->ptr();
|
||||||
|
|
||||||
if (dec >= NOT_FIXED_DEC)
|
if (dec >= NOT_FIXED_DEC)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
Let's try to pretty print a floating point number. Here we use
|
|
||||||
'%-*.*g' conversion string:
|
|
||||||
'-' stands for left-padding with spaces, if such padding will take
|
|
||||||
place
|
|
||||||
'*' is a placeholder for the first argument, field_length, and
|
|
||||||
signifies minimal width of result string. If result is less than
|
|
||||||
field length it will be space-padded. Note, however, that we'll not
|
|
||||||
pass spaces to Field_string::store(const char *, ...), due to
|
|
||||||
strcend in the next line.
|
|
||||||
'.*' is a placeholder for DBL_DIG and defines maximum number of
|
|
||||||
significant digits in the result string. DBL_DIG is a hardware
|
|
||||||
specific C define for maximum number of decimal digits of a floating
|
|
||||||
point number, such that rounding to hardware floating point
|
|
||||||
representation and back to decimal will not lead to loss of
|
|
||||||
precision. I.e if DBL_DIG is 15, number 123456789111315 can be
|
|
||||||
represented as double without precision loss. As one can judge from
|
|
||||||
this description, chosing DBL_DIG here is questionable, especially
|
|
||||||
because it introduces a system dependency.
|
|
||||||
'g' means that conversion will use [-]ddd.ddd (conventional) style,
|
|
||||||
and fall back to [-]d.ddde[+|i]ddd (scientific) style if there is no
|
|
||||||
enough space for all digits.
|
|
||||||
Maximum length of result string (not counting spaces) is (I guess)
|
|
||||||
DBL_DIG + 8, where 8 is 1 for sign, 1 for decimal point, 1 for
|
|
||||||
exponent sign, 1 for exponent, and 4 for exponent value.
|
|
||||||
XXX: why do we use space-padding and trim spaces in the next line?
|
|
||||||
*/
|
|
||||||
sprintf(to,"%-*.*g",(int) field_length,DBL_DIG,nr);
|
sprintf(to,"%-*.*g",(int) field_length,DBL_DIG,nr);
|
||||||
to=strcend(to,' ');
|
to=strcend(to,' ');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef HAVE_FCONVERT
|
#ifdef HAVE_FCONVERT
|
||||||
char buff[320],*pos=buff;
|
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE],
|
||||||
|
char *pos= buff;
|
||||||
int decpt,sign,tmp_dec=dec;
|
int decpt,sign,tmp_dec=dec;
|
||||||
|
|
||||||
VOID(fconvert(nr,tmp_dec,&decpt,&sign,buff));
|
VOID(fconvert(nr,tmp_dec,&decpt,&sign,buff));
|
||||||
@@ -3721,13 +3698,50 @@ void Field_string::store(const char *from,uint length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Store double value in Field_string or Field_varstring.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
store_double_in_string_field()
|
||||||
|
field field to store value in
|
||||||
|
field_length number of characters in the field
|
||||||
|
nr number
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Pretty prints double number into field_length characters buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void store_double_in_string_field(Field_str *field, uint32 field_length,
|
||||||
|
double nr)
|
||||||
|
{
|
||||||
|
bool use_scientific_notation=TRUE;
|
||||||
|
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
|
||||||
|
int length;
|
||||||
|
if (field_length < 32 && nr > 1)
|
||||||
|
{
|
||||||
|
if (field->ceiling == 0)
|
||||||
|
{
|
||||||
|
static double e[]= {1e1, 1e2, 1e4, 1e8, 1e16 };
|
||||||
|
double p= 1;
|
||||||
|
for (int i= sizeof(e)/sizeof(e[0]), j= 1<<i ; j; i--, j>>= 1 )
|
||||||
|
{
|
||||||
|
if (field_length & j)
|
||||||
|
p*= e[i];
|
||||||
|
}
|
||||||
|
field->ceiling= p-1;
|
||||||
|
}
|
||||||
|
use_scientific_notation= (field->ceiling < nr);
|
||||||
|
}
|
||||||
|
length= sprintf(buff, "%-.*g",
|
||||||
|
use_scientific_notation ? max(0,field_length-5) : field_length,
|
||||||
|
nr);
|
||||||
|
DBUG_ASSERT(length <= field_length);
|
||||||
|
field->store(buff, (uint) length);
|
||||||
|
}
|
||||||
|
|
||||||
void Field_string::store(double nr)
|
void Field_string::store(double nr)
|
||||||
{
|
{
|
||||||
char buff[MAX_FIELD_WIDTH],*end;
|
store_double_in_string_field(this, field_length, nr);
|
||||||
int width=min(field_length,DBL_DIG+5);
|
|
||||||
sprintf(buff,"%-*.*g",width,max(width-5,0),nr);
|
|
||||||
end=strcend(buff,' ');
|
|
||||||
Field_string::store(buff,(uint) (end - buff));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3927,11 +3941,7 @@ void Field_varstring::store(const char *from,uint length)
|
|||||||
|
|
||||||
void Field_varstring::store(double nr)
|
void Field_varstring::store(double nr)
|
||||||
{
|
{
|
||||||
char buff[MAX_FIELD_WIDTH],*end;
|
store_double_in_string_field(this, field_length, nr);
|
||||||
int width=min(field_length,DBL_DIG+5);
|
|
||||||
sprintf(buff,"%-*.*g",width,max(width-5,0),nr);
|
|
||||||
end=strcend(buff,' ');
|
|
||||||
Field_varstring::store(buff,(uint) (end - buff));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -255,12 +255,13 @@ public:
|
|||||||
|
|
||||||
class Field_str :public Field {
|
class Field_str :public Field {
|
||||||
public:
|
public:
|
||||||
|
double ceiling; // for ::store(double nr)
|
||||||
Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
|
Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
|
||||||
uchar null_bit_arg, utype unireg_check_arg,
|
uchar null_bit_arg, utype unireg_check_arg,
|
||||||
const char *field_name_arg,
|
const char *field_name_arg,
|
||||||
struct st_table *table_arg)
|
struct st_table *table_arg)
|
||||||
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
|
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
|
||||||
unireg_check_arg, field_name_arg, table_arg)
|
unireg_check_arg, field_name_arg, table_arg), ceiling(0.0)
|
||||||
{}
|
{}
|
||||||
Item_result result_type () const { return STRING_RESULT; }
|
Item_result result_type () const { return STRING_RESULT; }
|
||||||
uint decimals() const { return NOT_FIXED_DEC; }
|
uint decimals() const { return NOT_FIXED_DEC; }
|
||||||
|
Reference in New Issue
Block a user