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 | |
42 | int 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 | |
98 | int 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 | */ |
152 | bool |
153 | str_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 | |
202 | int 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 | |
241 | int 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 | */ |
273 | bool 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 | */ |
308 | my_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 | |
323 | my_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 | |
332 | void 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 | |
347 | int 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 */ |
375 | void |
376 | print_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 */ |
392 | void |
393 | print_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 | |
405 | const 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 | |