| 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 | |