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