~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to sql/my_decimal.cc

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2005-2006 MySQL AB
 
2
 
 
3
   This program is free software; you can redistribute it and/or modify
 
4
   it under the terms of the GNU General Public License as published by
 
5
   the Free Software Foundation; version 2 of the License.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
 
 
12
   You should have received a copy of the GNU General Public License
 
13
   along with this program; if not, write to the Free Software
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
#include "mysql_priv.h"
 
17
#include <time.h>
 
18
 
 
19
 
 
20
#ifndef MYSQL_CLIENT
 
21
/**
 
22
  report result of decimal operation.
 
23
 
 
24
  @param result  decimal library return code (E_DEC_* see include/decimal.h)
 
25
 
 
26
  @todo
 
27
    Fix error messages
 
28
 
 
29
  @return
 
30
    result
 
31
*/
 
32
 
 
33
int decimal_operation_results(int result)
 
34
{
 
35
  switch (result) {
 
36
  case E_DEC_OK:
 
37
    break;
 
38
  case E_DEC_TRUNCATED:
 
39
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
40
                        WARN_DATA_TRUNCATED, ER(WARN_DATA_TRUNCATED),
 
41
                        "", (long)-1);
 
42
    break;
 
43
  case E_DEC_OVERFLOW:
 
44
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
 
45
                        ER_TRUNCATED_WRONG_VALUE,
 
46
                        ER(ER_TRUNCATED_WRONG_VALUE),
 
47
                        "DECIMAL", "");
 
48
    break;
 
49
  case E_DEC_DIV_ZERO:
 
50
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
 
51
                        ER_DIVISION_BY_ZERO, ER(ER_DIVISION_BY_ZERO));
 
52
    break;
 
53
  case E_DEC_BAD_NUM:
 
54
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
 
55
                        ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
 
56
                        ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
 
57
                        "decimal", "", "", (long)-1);
 
58
    break;
 
59
  case E_DEC_OOM:
 
60
    my_error(ER_OUT_OF_RESOURCES, MYF(0));
 
61
    break;
 
62
  default:
 
63
    DBUG_ASSERT(0);
 
64
  }
 
65
  return result;
 
66
}
 
67
 
 
68
 
 
69
/**
 
70
  @brief Converting decimal to string
 
71
 
 
72
  @details Convert given my_decimal to String; allocate buffer as needed.
 
73
 
 
74
  @param[in]   mask        what problems to warn on (mask of E_DEC_* values)
 
75
  @param[in]   d           the decimal to print
 
76
  @param[in]   fixed_prec  overall number of digits if ZEROFILL, 0 otherwise
 
77
  @param[in]   fixed_dec   number of decimal places (if fixed_prec != 0)
 
78
  @param[in]   filler      what char to pad with (ZEROFILL et al.)
 
79
  @param[out]  *str        where to store the resulting string
 
80
 
 
81
  @return error coce
 
82
    @retval E_DEC_OK
 
83
    @retval E_DEC_TRUNCATED
 
84
    @retval E_DEC_OVERFLOW
 
85
    @retval E_DEC_OOM
 
86
*/
 
87
 
 
88
int my_decimal2string(uint mask, const my_decimal *d,
 
89
                      uint fixed_prec, uint fixed_dec,
 
90
                      char filler, String *str)
 
91
{
 
92
  /*
 
93
    Calculate the size of the string: For DECIMAL(a,b), fixed_prec==a
 
94
    holds true iff the type is also ZEROFILL, which in turn implies
 
95
    UNSIGNED. Hence the buffer for a ZEROFILLed value is the length
 
96
    the user requested, plus one for a possible decimal point, plus
 
97
    one if the user only wanted decimal places, but we force a leading
 
98
    zero on them. Because the type is implicitly UNSIGNED, we do not
 
99
    need to reserve a character for the sign. For all other cases,
 
100
    fixed_prec will be 0, and my_decimal_string_length() will be called
 
101
    instead to calculate the required size of the buffer.
 
102
  */
 
103
  int length= (fixed_prec
 
104
               ? (fixed_prec + ((fixed_prec == fixed_dec) ? 1 : 0) + 1)
 
105
               : my_decimal_string_length(d));
 
106
  int result;
 
107
  if (str->alloc(length))
 
108
    return check_result(mask, E_DEC_OOM);
 
109
  result= decimal2string((decimal_t*) d, (char*) str->ptr(),
 
110
                         &length, (int)fixed_prec, fixed_dec,
 
111
                         filler);
 
112
  str->length(length);
 
113
  return check_result(mask, result);
 
114
}
 
115
 
 
116
 
 
117
/*
 
118
  Convert from decimal to binary representation
 
119
 
 
120
  SYNOPSIS
 
121
    my_decimal2binary()
 
122
    mask        error processing mask
 
123
    d           number for conversion
 
124
    bin         pointer to buffer where to write result
 
125
    prec        overall number of decimal digits
 
126
    scale       number of decimal digits after decimal point
 
127
 
 
128
  NOTE
 
129
    Before conversion we round number if it need but produce truncation
 
130
    error in this case
 
131
 
 
132
  RETURN
 
133
    E_DEC_OK
 
134
    E_DEC_TRUNCATED
 
135
    E_DEC_OVERFLOW
 
136
*/
 
137
 
 
138
int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec,
 
139
                      int scale)
 
140
{
 
141
  int err1= E_DEC_OK, err2;
 
142
  my_decimal rounded;
 
143
  my_decimal2decimal(d, &rounded);
 
144
  rounded.frac= decimal_actual_fraction(&rounded);
 
145
  if (scale < rounded.frac)
 
146
  {
 
147
    err1= E_DEC_TRUNCATED;
 
148
    /* decimal_round can return only E_DEC_TRUNCATED */
 
149
    decimal_round(&rounded, &rounded, scale, HALF_UP);
 
150
  }
 
151
  err2= decimal2bin(&rounded, bin, prec, scale);
 
152
  if (!err2)
 
153
    err2= err1;
 
154
  return check_result(mask, err2);
 
155
}
 
156
 
 
157
 
 
158
/*
 
159
  Convert string for decimal when string can be in some multibyte charset
 
160
 
 
161
  SYNOPSIS
 
162
    str2my_decimal()
 
163
    mask            error processing mask
 
164
    from            string to process
 
165
    length          length of given string
 
166
    charset         charset of given string
 
167
    decimal_value   buffer for result storing
 
168
 
 
169
  RESULT
 
170
    E_DEC_OK
 
171
    E_DEC_TRUNCATED
 
172
    E_DEC_OVERFLOW
 
173
    E_DEC_BAD_NUM
 
174
    E_DEC_OOM
 
175
*/
 
176
 
 
177
int str2my_decimal(uint mask, const char *from, uint length,
 
178
                   CHARSET_INFO *charset, my_decimal *decimal_value)
 
179
{
 
180
  char *end, *from_end;
 
181
  int err;
 
182
  char buff[STRING_BUFFER_USUAL_SIZE];
 
183
  String tmp(buff, sizeof(buff), &my_charset_bin);
 
184
  if (charset->mbminlen > 1)
 
185
  {
 
186
    uint dummy_errors;
 
187
    tmp.copy(from, length, charset, &my_charset_latin1, &dummy_errors);
 
188
    from= tmp.ptr();
 
189
    length=  tmp.length();
 
190
    charset= &my_charset_bin;
 
191
  }
 
192
  from_end= end= (char*) from+length;
 
193
  err= string2decimal((char *)from, (decimal_t*) decimal_value, &end);
 
194
  if (end != from_end && !err)
 
195
  {
 
196
    /* Give warning if there is something other than end space */
 
197
    for ( ; end < from_end; end++)
 
198
    {
 
199
      if (!my_isspace(&my_charset_latin1, *end))
 
200
      {
 
201
        err= E_DEC_TRUNCATED;
 
202
        break;
 
203
      }
 
204
    }
 
205
  }
 
206
  check_result_and_overflow(mask, err, decimal_value);
 
207
  return err;
 
208
}
 
209
 
 
210
 
 
211
my_decimal *date2my_decimal(MYSQL_TIME *ltime, my_decimal *dec)
 
212
{
 
213
  longlong date;
 
214
  date = (ltime->year*100L + ltime->month)*100L + ltime->day;
 
215
  if (ltime->time_type > MYSQL_TIMESTAMP_DATE)
 
216
    date= ((date*100L + ltime->hour)*100L+ ltime->minute)*100L + ltime->second;
 
217
  if (int2my_decimal(E_DEC_FATAL_ERROR, date, FALSE, dec))
 
218
    return dec;
 
219
  if (ltime->second_part)
 
220
  {
 
221
    dec->buf[(dec->intg-1) / 9 + 1]= ltime->second_part * 1000;
 
222
    dec->frac= 6;
 
223
  }
 
224
  return dec;
 
225
}
 
226
 
 
227
 
 
228
void my_decimal_trim(ulong *precision, uint *scale)
 
229
{
 
230
  if (!(*precision) && !(*scale))
 
231
  {
 
232
    *precision= 10;
 
233
    *scale= 0;
 
234
    return;
 
235
  }
 
236
}
 
237
 
 
238
 
 
239
#ifndef DBUG_OFF
 
240
/* routines for debugging print */
 
241
 
 
242
#define DIG_PER_DEC1 9
 
243
#define ROUND_UP(X)  (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1)
 
244
 
 
245
/* print decimal */
 
246
void
 
247
print_decimal(const my_decimal *dec)
 
248
{
 
249
  int i, end;
 
250
  char buff[512], *pos;
 
251
  pos= buff;
 
252
  pos+= my_sprintf(buff, (buff, "Decimal: sign: %d  intg: %d  frac: %d  { ",
 
253
                          dec->sign(), dec->intg, dec->frac));
 
254
  end= ROUND_UP(dec->frac)+ROUND_UP(dec->intg)-1;
 
255
  for (i=0; i < end; i++)
 
256
    pos+= my_sprintf(pos, (pos, "%09d, ", dec->buf[i]));
 
257
  pos+= my_sprintf(pos, (pos, "%09d }\n", dec->buf[i]));
 
258
  fputs(buff, DBUG_FILE);
 
259
}
 
260
 
 
261
 
 
262
/* print decimal with its binary representation */
 
263
void
 
264
print_decimal_buff(const my_decimal *dec, const uchar* ptr, int length)
 
265
{
 
266
  print_decimal(dec);
 
267
  fprintf(DBUG_FILE, "Record: ");
 
268
  for (int i= 0; i < length; i++)
 
269
  {
 
270
    fprintf(DBUG_FILE, "%02X ", (uint)((uchar *)ptr)[i]);
 
271
  }
 
272
  fprintf(DBUG_FILE, "\n");
 
273
}
 
274
 
 
275
 
 
276
const char *dbug_decimal_as_string(char *buff, const my_decimal *val)
 
277
{
 
278
  int length= DECIMAL_MAX_STR_LENGTH;
 
279
  if (!val)
 
280
    return "NULL";
 
281
  (void)decimal2string((decimal_t*) val, buff, &length, 0,0,0);
 
282
  return buff;
 
283
}
 
284
 
 
285
#endif /*DBUG_OFF*/
 
286
 
 
287
 
 
288
#endif /*MYSQL_CLIENT*/