Commit 8c83ab40 authored by unknown's avatar unknown

Bug 4937: different date -> string conversion when using

SELECT ... UNION and INSERT ... SELECT ... UNION
parent e20b1a57
...@@ -79,3 +79,20 @@ SELECT DATE_FORMAT(f1, "%l.%i %p") , DATE_FORMAT(f2, "%l.%i %p") FROM t1; ...@@ -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; ...@@ -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;
...@@ -37,6 +37,7 @@ ...@@ -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>; ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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 @@ class Field_num :public Field { ...@@ -255,12 +255,13 @@ class Field_num :public Field {
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; }
......
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