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