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