summaryrefslogtreecommitdiff
path: root/sql/field.cc
diff options
context:
space:
mode:
authorunknown <serg@serg.mylan>2004-08-18 14:46:31 +0200
committerunknown <serg@serg.mylan>2004-08-18 14:46:31 +0200
commit0c6b96658e96cc604c9c5fedb41a5e43bad4f74b (patch)
treed50bf741dd1cb34a20cac2d5301d726d50010a3a /sql/field.cc
parent8ddcba9d8a85fd8726c42a555e91a8dd52ba9cd3 (diff)
downloadmariadb-git-0c6b96658e96cc604c9c5fedb41a5e43bad4f74b.tar.gz
Bug 4937: different date -> string conversion when using
SELECT ... UNION and INSERT ... SELECT ... UNION
Diffstat (limited to 'sql/field.cc')
-rw-r--r--sql/field.cc90
1 files changed, 50 insertions, 40 deletions
diff --git a/sql/field.cc b/sql/field.cc
index aca1f8846f0..33717d99583 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -37,6 +37,7 @@
#include "sql_select.h"
#include <m_ctype.h>
#include <errno.h>
+#include <assert.h>
#ifdef HAVE_FCONVERT
#include <floatingpoint.h>
#endif
@@ -58,6 +59,8 @@ template class List_iterator<create_field>;
uchar Field_null::null[1]={1};
const char field_separator=',';
+#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE 320
+
/*****************************************************************************
Static help functions
*****************************************************************************/
@@ -739,7 +742,7 @@ void Field_decimal::store(double nr)
reg4 uint i,length;
char fyllchar,*to;
- char buff[320];
+ char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
fyllchar = zerofill ? (char) '0' : (char) ' ';
#ifdef HAVE_SNPRINTF
@@ -2326,46 +2329,20 @@ String *Field_double::val_str(String *val_buffer,
#endif
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);
char *to=(char*) val_buffer->ptr();
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);
to=strcend(to,' ');
}
else
{
#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;
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)
{
- char buff[MAX_FIELD_WIDTH],*end;
- 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));
+ store_double_in_string_field(this, field_length, nr);
}
@@ -3927,11 +3941,7 @@ void Field_varstring::store(const char *from,uint length)
void Field_varstring::store(double nr)
{
- char buff[MAX_FIELD_WIDTH],*end;
- 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));
+ store_double_in_string_field(this, field_length, nr);
}