MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
my_decimal.cc
1 /* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include <my_global.h>
17 #include "sql_priv.h"
18 #include <time.h>
19 
20 #ifndef MYSQL_CLIENT
21 #include "sql_class.h" // THD
22 #endif
23 
24 #ifndef MYSQL_CLIENT
25 
37 int decimal_operation_results(int result)
38 {
39  switch (result) {
40  case E_DEC_OK:
41  break;
42  case E_DEC_TRUNCATED:
43  push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
44  WARN_DATA_TRUNCATED, ER(WARN_DATA_TRUNCATED),
45  "", (long)-1);
46  break;
47  case E_DEC_OVERFLOW:
48  push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
49  ER_TRUNCATED_WRONG_VALUE,
50  ER(ER_TRUNCATED_WRONG_VALUE),
51  "DECIMAL", "");
52  break;
53  case E_DEC_DIV_ZERO:
54  push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
55  ER_DIVISION_BY_ZERO, ER(ER_DIVISION_BY_ZERO));
56  break;
57  case E_DEC_BAD_NUM:
58  push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
59  ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
60  ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
61  "decimal", "", "", (long)-1);
62  break;
63  case E_DEC_OOM:
64  my_error(ER_OUT_OF_RESOURCES, MYF(0));
65  break;
66  default:
67  DBUG_ASSERT(0);
68  }
69  return result;
70 }
71 
72 
92 int my_decimal2string(uint mask, const my_decimal *d,
93  uint fixed_prec, uint fixed_dec,
94  char filler, String *str)
95 {
96  /*
97  Calculate the size of the string: For DECIMAL(a,b), fixed_prec==a
98  holds true iff the type is also ZEROFILL, which in turn implies
99  UNSIGNED. Hence the buffer for a ZEROFILLed value is the length
100  the user requested, plus one for a possible decimal point, plus
101  one if the user only wanted decimal places, but we force a leading
102  zero on them, plus one for the '\0' terminator. Because the type
103  is implicitly UNSIGNED, we do not need to reserve a character for
104  the sign. For all other cases, fixed_prec will be 0, and
105  my_decimal_string_length() will be called instead to calculate the
106  required size of the buffer.
107  */
108  int length= (fixed_prec
109  ? (fixed_prec + ((fixed_prec == fixed_dec) ? 1 : 0) + 1 + 1)
110  : my_decimal_string_length(d));
111  int result;
112  if (str->alloc(length))
113  return check_result(mask, E_DEC_OOM);
114  result= decimal2string((decimal_t*) d, (char*) str->ptr(),
115  &length, (int)fixed_prec, fixed_dec,
116  filler);
117  str->length(length);
118  str->set_charset(&my_charset_numeric);
119  return check_result(mask, result);
120 }
121 
122 
146 bool
147 str_set_decimal(uint mask, const my_decimal *val,
148  uint fixed_prec, uint fixed_dec, char filler,
149  String *str, const CHARSET_INFO *cs)
150 {
151  if (!(cs->state & MY_CS_NONASCII))
152  {
153  /* For ASCII-compatible character sets we can use my_decimal2string */
154  my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, str);
155  str->set_charset(cs);
156  return FALSE;
157  }
158  else
159  {
160  /*
161  For ASCII-incompatible character sets (like UCS2) we
162  call my_decimal2string() on a temporary buffer first,
163  and then convert the result to the target character
164  with help of str->copy().
165  */
166  uint errors;
168  String tmp(buf, sizeof(buf), &my_charset_latin1);
169  my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, &tmp);
170  return str->copy(tmp.ptr(), tmp.length(), &my_charset_latin1, cs, &errors);
171  }
172 }
173 
174 
175 /*
176  Convert from decimal to binary representation
177 
178  SYNOPSIS
179  my_decimal2binary()
180  mask error processing mask
181  d number for conversion
182  bin pointer to buffer where to write result
183  prec overall number of decimal digits
184  scale number of decimal digits after decimal point
185 
186  NOTE
187  Before conversion we round number if it need but produce truncation
188  error in this case
189 
190  RETURN
191  E_DEC_OK
192  E_DEC_TRUNCATED
193  E_DEC_OVERFLOW
194 */
195 
196 int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec,
197  int scale)
198 {
199  int err1= E_DEC_OK, err2;
200  my_decimal rounded;
201  my_decimal2decimal(d, &rounded);
202  rounded.frac= decimal_actual_fraction(&rounded);
203  if (scale < rounded.frac)
204  {
205  err1= E_DEC_TRUNCATED;
206  /* decimal_round can return only E_DEC_TRUNCATED */
207  decimal_round(&rounded, &rounded, scale, HALF_UP);
208  }
209  err2= decimal2bin(&rounded, bin, prec, scale);
210  if (!err2)
211  err2= err1;
212  return check_result(mask, err2);
213 }
214 
215 
216 /*
217  Convert string for decimal when string can be in some multibyte charset
218 
219  SYNOPSIS
220  str2my_decimal()
221  mask error processing mask
222  from string to process
223  length length of given string
224  charset charset of given string
225  decimal_value buffer for result storing
226 
227  RESULT
228  E_DEC_OK
229  E_DEC_TRUNCATED
230  E_DEC_OVERFLOW
231  E_DEC_BAD_NUM
232  E_DEC_OOM
233 */
234 
235 int str2my_decimal(uint mask, const char *from, uint length,
236  const CHARSET_INFO *charset, my_decimal *decimal_value)
237 {
238  char *end, *from_end;
239  int err;
240  char buff[STRING_BUFFER_USUAL_SIZE];
241  String tmp(buff, sizeof(buff), &my_charset_bin);
242  if (charset->mbminlen > 1)
243  {
244  uint dummy_errors;
245  tmp.copy(from, length, charset, &my_charset_latin1, &dummy_errors);
246  from= tmp.ptr();
247  length= tmp.length();
248  charset= &my_charset_bin;
249  }
250  from_end= end= (char*) from+length;
251  err= string2decimal((char *)from, (decimal_t*) decimal_value, &end);
252  if (end != from_end && !err)
253  {
254  /* Give warning if there is something other than end space */
255  for ( ; end < from_end; end++)
256  {
257  if (!my_isspace(&my_charset_latin1, *end))
258  {
259  err= E_DEC_TRUNCATED;
260  break;
261  }
262  }
263  }
264  check_result_and_overflow(mask, err, decimal_value);
265  return err;
266 }
267 
268 
278 static my_decimal *lldiv_t2my_decimal(const lldiv_t *lld, bool neg,
279  my_decimal *dec)
280 {
281  if (int2my_decimal(E_DEC_FATAL_ERROR, lld->quot, FALSE, dec))
282  return dec;
283  if (neg)
284  decimal_neg((decimal_t *) dec);
285  if (lld->rem)
286  {
287  dec->buf[(dec->intg-1) / 9 + 1]= lld->rem;
288  dec->frac= 6;
289  }
290  return dec;
291 }
292 
293 
299 my_decimal *date2my_decimal(const MYSQL_TIME *ltime, my_decimal *dec)
300 {
301  lldiv_t lld;
302  lld.quot= ltime->time_type > MYSQL_TIMESTAMP_DATE ?
303  TIME_to_ulonglong_datetime(ltime) :
304  TIME_to_ulonglong_date(ltime);
305  lld.rem= (longlong) ltime->second_part * 1000;
306  return lldiv_t2my_decimal(&lld, ltime->neg, dec);
307 }
308 
309 
315 my_decimal *time2my_decimal(const MYSQL_TIME *ltime, my_decimal *dec)
316 {
317  lldiv_t lld;
318  lld.quot= TIME_to_ulonglong_time(ltime);
319  lld.rem= (longlong) ltime->second_part * 1000;
320  return lldiv_t2my_decimal(&lld, ltime->neg, dec);
321 }
322 
323 
327 my_decimal *timeval2my_decimal(const struct timeval *tm, my_decimal *dec)
328 {
329  lldiv_t lld;
330  lld.quot= tm->tv_sec;
331  lld.rem= (longlong) tm->tv_usec * 1000;
332  return lldiv_t2my_decimal(&lld, 0, dec);
333 }
334 
335 
336 void my_decimal_trim(ulong *precision, uint *scale)
337 {
338  if (!(*precision) && !(*scale))
339  {
340  *precision= 10;
341  *scale= 0;
342  return;
343  }
344 }
345 
346 
347 #ifndef DBUG_OFF
348 /* routines for debugging print */
349 
350 #define DIG_PER_DEC1 9
351 #define ROUND_UP(X) (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1)
352 
353 /* print decimal */
354 void
355 print_decimal(const my_decimal *dec)
356 {
357  int i, end;
358  char buff[512], *pos;
359  pos= buff;
360  pos+= sprintf(buff, "Decimal: sign: %d intg: %d frac: %d { ",
361  dec->sign(), dec->intg, dec->frac);
362  end= ROUND_UP(dec->frac)+ROUND_UP(dec->intg)-1;
363  for (i=0; i < end; i++)
364  pos+= sprintf(pos, "%09d, ", dec->buf[i]);
365  pos+= sprintf(pos, "%09d }\n", dec->buf[i]);
366  fputs(buff, DBUG_FILE);
367 }
368 
369 
370 /* print decimal with its binary representation */
371 void
372 print_decimal_buff(const my_decimal *dec, const uchar* ptr, int length)
373 {
374  print_decimal(dec);
375  fprintf(DBUG_FILE, "Record: ");
376  for (int i= 0; i < length; i++)
377  {
378  fprintf(DBUG_FILE, "%02X ", (uint)((uchar *)ptr)[i]);
379  }
380  fprintf(DBUG_FILE, "\n");
381 }
382 
383 
384 const char *dbug_decimal_as_string(char *buff, const my_decimal *val)
385 {
386  int length= DECIMAL_MAX_STR_LENGTH + 1; /* minimum size for buff */
387  if (!val)
388  return "NULL";
389  (void)decimal2string((decimal_t*) val, buff, &length, 0,0,0);
390  return buff;
391 }
392 
393 #endif /*DBUG_OFF*/
394 
395 
396 #endif /*MYSQL_CLIENT*/