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, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #include <my_global.h>
24 #include "sql_priv.h"
25 #include <time.h>
26 
27 #ifndef MYSQL_CLIENT
28 #include "sql_class.h"                          // THD
29 #endif
30 
31 #ifndef MYSQL_CLIENT
32 /**
33   report result of decimal operation.
34 
35   @param result  decimal library return code (E_DEC_* see include/decimal.h)
36 
37   @todo
38     Fix error messages
39 
40   @return
41     result
42 */
43 
decimal_operation_results(int result)44 int decimal_operation_results(int result)
45 {
46   switch (result) {
47   case E_DEC_OK:
48     break;
49   case E_DEC_TRUNCATED:
50     push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
51 			WARN_DATA_TRUNCATED, ER(WARN_DATA_TRUNCATED),
52 			"", (long)-1);
53     break;
54   case E_DEC_OVERFLOW:
55     push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
56                         ER_TRUNCATED_WRONG_VALUE,
57                         ER(ER_TRUNCATED_WRONG_VALUE),
58 			"DECIMAL", "");
59     break;
60   case E_DEC_DIV_ZERO:
61     push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
62 			ER_DIVISION_BY_ZERO, ER(ER_DIVISION_BY_ZERO));
63     break;
64   case E_DEC_BAD_NUM:
65     push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
66 			ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
67 			ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
68 			"decimal", "", "", (long)-1);
69     break;
70   case E_DEC_OOM:
71     my_error(ER_OUT_OF_RESOURCES, MYF(0));
72     break;
73   default:
74     DBUG_ASSERT(0);
75   }
76   return result;
77 }
78 
79 
80 /**
81   @brief Converting decimal to string
82 
83   @details Convert given my_decimal to String; allocate buffer as needed.
84 
85   @param[in]   mask        what problems to warn on (mask of E_DEC_* values)
86   @param[in]   d           the decimal to print
87   @param[in]   fixed_prec  overall number of digits if ZEROFILL, 0 otherwise
88   @param[in]   fixed_dec   number of decimal places (if fixed_prec != 0)
89   @param[in]   filler      what char to pad with (ZEROFILL et al.)
90   @param[out]  *str        where to store the resulting string
91 
92   @return error coce
93     @retval E_DEC_OK
94     @retval E_DEC_TRUNCATED
95     @retval E_DEC_OVERFLOW
96     @retval E_DEC_OOM
97 */
98 
my_decimal2string(uint mask,const my_decimal * d,uint fixed_prec,uint fixed_dec,char filler,String * str)99 int my_decimal2string(uint mask, const my_decimal *d,
100                       uint fixed_prec, uint fixed_dec,
101                       char filler, String *str)
102 {
103   /*
104     Calculate the size of the string: For DECIMAL(a,b), fixed_prec==a
105     holds true iff the type is also ZEROFILL, which in turn implies
106     UNSIGNED. Hence the buffer for a ZEROFILLed value is the length
107     the user requested, plus one for a possible decimal point, plus
108     one if the user only wanted decimal places, but we force a leading
109     zero on them, plus one for the '\0' terminator. Because the type
110     is implicitly UNSIGNED, we do not need to reserve a character for
111     the sign. For all other cases, fixed_prec will be 0, and
112     my_decimal_string_length() will be called instead to calculate the
113     required size of the buffer.
114   */
115   int length= (fixed_prec
116                ? (fixed_prec + ((fixed_prec == fixed_dec) ? 1 : 0) + 1 + 1)
117                : my_decimal_string_length(d));
118   int result;
119   if (str->alloc(length))
120     return check_result(mask, E_DEC_OOM);
121   result= decimal2string((decimal_t*) d, (char*) str->ptr(),
122                          &length, (int)fixed_prec, fixed_dec,
123                          filler);
124   str->length(length);
125   str->set_charset(&my_charset_numeric);
126   return check_result(mask, result);
127 }
128 
129 
130 /**
131   @brief Converting decimal to string with character set conversion
132 
133   @details Convert given my_decimal to String; allocate buffer as needed.
134 
135   @param[in]   mask        what problems to warn on (mask of E_DEC_* values)
136   @param[in]   val         the decimal to print
137   @param[in]   fixed_prec  overall number of digits if ZEROFILL, 0 otherwise
138   @param[in]   fixed_dec   number of decimal places (if fixed_prec != 0)
139   @param[in]   filler      what char to pad with (ZEROFILL et al.)
140   @param[out]  *str        where to store the resulting string
141   @param[in]   cs          character set
142 
143   @return error coce
144     @retval E_DEC_OK
145     @retval E_DEC_TRUNCATED
146     @retval E_DEC_OVERFLOW
147     @retval E_DEC_OOM
148 
149   Would be great to make it a method of the String class,
150   but this would need to include
151   my_decimal.h from sql_string.h and sql_string.cc, which is not desirable.
152 */
153 bool
str_set_decimal(uint mask,const my_decimal * val,uint fixed_prec,uint fixed_dec,char filler,String * str,const CHARSET_INFO * cs)154 str_set_decimal(uint mask, const my_decimal *val,
155                 uint fixed_prec, uint fixed_dec, char filler,
156                 String *str, const CHARSET_INFO *cs)
157 {
158   if (!(cs->state & MY_CS_NONASCII))
159   {
160     /* For ASCII-compatible character sets we can use my_decimal2string */
161     my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, str);
162     str->set_charset(cs);
163     return FALSE;
164   }
165   else
166   {
167     /*
168       For ASCII-incompatible character sets (like UCS2) we
169       call my_decimal2string() on a temporary buffer first,
170       and then convert the result to the target character
171       with help of str->copy().
172     */
173     uint errors;
174     char buf[DECIMAL_MAX_STR_LENGTH];
175     String tmp(buf, sizeof(buf), &my_charset_latin1);
176     my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, &tmp);
177     return str->copy(tmp.ptr(), tmp.length(), &my_charset_latin1, cs, &errors);
178   }
179 }
180 
181 
182 /*
183   Convert from decimal to binary representation
184 
185   SYNOPSIS
186     my_decimal2binary()
187     mask        error processing mask
188     d           number for conversion
189     bin         pointer to buffer where to write result
190     prec        overall number of decimal digits
191     scale       number of decimal digits after decimal point
192 
193   NOTE
194     Before conversion we round number if it need but produce truncation
195     error in this case
196 
197   RETURN
198     E_DEC_OK
199     E_DEC_TRUNCATED
200     E_DEC_OVERFLOW
201 */
202 
my_decimal2binary(uint mask,const my_decimal * d,uchar * bin,int prec,int scale)203 int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec,
204 		      int scale)
205 {
206   int err1= E_DEC_OK, err2;
207   my_decimal rounded;
208   my_decimal2decimal(d, &rounded);
209   rounded.frac= decimal_actual_fraction(&rounded);
210   if (scale < rounded.frac)
211   {
212     err1= E_DEC_TRUNCATED;
213     /* decimal_round can return only E_DEC_TRUNCATED */
214     decimal_round(&rounded, &rounded, scale, HALF_UP);
215   }
216   err2= decimal2bin(&rounded, bin, prec, scale);
217   if (!err2)
218     err2= err1;
219   return check_result(mask, err2);
220 }
221 
222 
223 /*
224   Convert string for decimal when string can be in some multibyte charset
225 
226   SYNOPSIS
227     str2my_decimal()
228     mask            error processing mask
229     from            string to process
230     length          length of given string
231     charset         charset of given string
232     decimal_value   buffer for result storing
233 
234   RESULT
235     E_DEC_OK
236     E_DEC_TRUNCATED
237     E_DEC_OVERFLOW
238     E_DEC_BAD_NUM
239     E_DEC_OOM
240 */
241 
str2my_decimal(uint mask,const char * from,uint length,const CHARSET_INFO * charset,my_decimal * decimal_value)242 int str2my_decimal(uint mask, const char *from, uint length,
243                    const CHARSET_INFO *charset, my_decimal *decimal_value)
244 {
245   char *end, *from_end;
246   int err;
247   char buff[STRING_BUFFER_USUAL_SIZE];
248   String tmp(buff, sizeof(buff), &my_charset_bin);
249   if (charset->mbminlen > 1)
250   {
251     uint dummy_errors;
252     tmp.copy(from, length, charset, &my_charset_latin1, &dummy_errors);
253     from= tmp.ptr();
254     length=  tmp.length();
255     charset= &my_charset_bin;
256   }
257   from_end= end= (char*) from+length;
258   err= string2decimal((char *)from, (decimal_t*) decimal_value, &end);
259   if (end != from_end && !err)
260   {
261     /* Give warning if there is something other than end space */
262     for ( ; end < from_end; end++)
263     {
264       if (!my_isspace(&my_charset_latin1, *end))
265       {
266         err= E_DEC_TRUNCATED;
267         break;
268       }
269     }
270   }
271   check_result_and_overflow(mask, err, decimal_value);
272   return err;
273 }
274 
275 
276 /**
277   Convert lldiv_t value to my_decimal value.
278   Integer part of the result is set to lld->quot.
279   Fractional part of the result is set to lld->rem divided to 1000000000.
280 
281   @param       lld  The lldiv_t variable to convert from.
282   @param       neg  Sign flag (negative, 0 positive).
283   @param  OUT  dec  Decimal numbert to convert to.
284 */
lldiv_t2my_decimal(const lldiv_t * lld,bool neg,my_decimal * dec)285 static my_decimal *lldiv_t2my_decimal(const lldiv_t *lld, bool neg,
286                                       my_decimal *dec)
287 {
288   if (int2my_decimal(E_DEC_FATAL_ERROR, lld->quot, FALSE, dec))
289     return dec;
290   if (neg)
291     decimal_neg((decimal_t *) dec);
292   if (lld->rem)
293   {
294     dec->buf[(dec->intg-1) / 9 + 1]= lld->rem;
295     dec->frac= 6;
296   }
297   return dec;
298 }
299 
300 
301 /**
302   Convert datetime value to my_decimal in format YYYYMMDDhhmmss.ffffff
303   @param ltime  Date value to convert from.
304   @param dec    Decimal value to convert to.
305 */
date2my_decimal(const MYSQL_TIME * ltime,my_decimal * dec)306 my_decimal *date2my_decimal(const MYSQL_TIME *ltime, my_decimal *dec)
307 {
308   lldiv_t lld;
309   lld.quot= ltime->time_type > MYSQL_TIMESTAMP_DATE ?
310             TIME_to_ulonglong_datetime(ltime) :
311             TIME_to_ulonglong_date(ltime);
312   lld.rem= (longlong) ltime->second_part * 1000;
313   return lldiv_t2my_decimal(&lld, ltime->neg, dec);
314 }
315 
316 
317 /**
318   Convert time value to my_decimal in format hhmmss.ffffff
319   @param ltime  Date value to convert from.
320   @param dec    Decimal value to convert to.
321 */
time2my_decimal(const MYSQL_TIME * ltime,my_decimal * dec)322 my_decimal *time2my_decimal(const MYSQL_TIME *ltime, my_decimal *dec)
323 {
324   lldiv_t lld;
325   lld.quot= TIME_to_ulonglong_time(ltime);
326   lld.rem= (longlong) ltime->second_part * 1000;
327   return lldiv_t2my_decimal(&lld, ltime->neg, dec);
328 }
329 
330 
331 /**
332   Convert timeval value to my_decimal.
333 */
timeval2my_decimal(const struct timeval * tm,my_decimal * dec)334 my_decimal *timeval2my_decimal(const struct timeval *tm, my_decimal *dec)
335 {
336   lldiv_t lld;
337   lld.quot= tm->tv_sec;
338   lld.rem= (longlong) tm->tv_usec * 1000;
339   return lldiv_t2my_decimal(&lld, 0, dec);
340 }
341 
342 
my_decimal_trim(ulong * precision,uint * scale)343 void my_decimal_trim(ulong *precision, uint *scale)
344 {
345   if (!(*precision) && !(*scale))
346   {
347     *precision= 10;
348     *scale= 0;
349     return;
350   }
351 }
352 
353 
354 #ifndef DBUG_OFF
355 /* routines for debugging print */
356 
357 #define DIG_PER_DEC1 9
358 #define ROUND_UP(X)  (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1)
359 
360 /* print decimal */
361 void
print_decimal(const my_decimal * dec)362 print_decimal(const my_decimal *dec)
363 {
364   int i, end;
365   char buff[512], *pos;
366   pos= buff;
367   pos+= sprintf(buff, "Decimal: sign: %d  intg: %d  frac: %d  { ",
368                 dec->sign(), dec->intg, dec->frac);
369   end= ROUND_UP(dec->frac)+ROUND_UP(dec->intg)-1;
370   for (i=0; i < end; i++)
371     pos+= sprintf(pos, "%09d, ", dec->buf[i]);
372   pos+= sprintf(pos, "%09d }\n", dec->buf[i]);
373   fputs(buff, DBUG_FILE);
374 }
375 
376 
377 /* print decimal with its binary representation */
378 void
print_decimal_buff(const my_decimal * dec,const uchar * ptr,int length)379 print_decimal_buff(const my_decimal *dec, const uchar* ptr, int length)
380 {
381   print_decimal(dec);
382   fprintf(DBUG_FILE, "Record: ");
383   for (int i= 0; i < length; i++)
384   {
385     fprintf(DBUG_FILE, "%02X ", (uint)((uchar *)ptr)[i]);
386   }
387   fprintf(DBUG_FILE, "\n");
388 }
389 
390 
dbug_decimal_as_string(char * buff,const my_decimal * val)391 const char *dbug_decimal_as_string(char *buff, const my_decimal *val)
392 {
393   int length= DECIMAL_MAX_STR_LENGTH + 1;     /* minimum size for buff */
394   if (!val)
395     return "NULL";
396   (void)decimal2string((decimal_t*) val, buff, &length, 0,0,0);
397   return buff;
398 }
399 
400 #endif /*DBUG_OFF*/
401 
402 
403 #endif /*MYSQL_CLIENT*/
404