Commit 0c6b9665 authored by unknown's avatar unknown
Browse files

Bug 4937: different date -> string conversion when using

SELECT ... UNION and INSERT ... SELECT ... UNION

parent 8ddcba9d
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -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")
9.00 AM	12.00 PM
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;
+19 −0
Original line number Diff line number Diff line
@@ -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');
SELECT DATE_FORMAT(f1, "%l.%i %p") , DATE_FORMAT(f2, "%l.%i %p") FROM 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;
+50 −40
Original line number Diff line number Diff line
@@ -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);
}


+2 −1
Original line number Diff line number Diff line
@@ -255,12 +255,13 @@ class Field_num :public Field {

class Field_str :public Field {
public:
  double ceiling; // for ::store(double nr)
  Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
	    uchar null_bit_arg, utype unireg_check_arg,
	    const char *field_name_arg,
	    struct st_table *table_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; }
  uint decimals() const { return NOT_FIXED_DEC; }