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