diff options
Diffstat (limited to 'sql/item_strfunc.cc')
-rw-r--r-- | sql/item_strfunc.cc | 728 |
1 files changed, 488 insertions, 240 deletions
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 27483ede032..f654d260564 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -30,7 +30,24 @@ #pragma implementation // gcc: Class implementation #endif -#include "mysql_priv.h" +/* May include caustic 3rd-party defs. Use early, so it can override nothing. */ +#include "sha2.h" +#include "my_global.h" // HAVE_* + + +#include "sql_priv.h" +/* + It is necessary to include set_var.h instead of item.h because there + are dependencies on include order for set_var.h and item.h. This + will be resolved later. +*/ +#include "sql_class.h" // set_var.h: THD +#include "set_var.h" +#include "mysqld.h" // LOCK_uuid_generator +#include "sql_acl.h" // SUPER_ACL +#include "des_key_file.h" // st_des_keyschedule, st_des_keyblock +#include "password.h" // my_make_scrambled_password, + // my_make_scrambled_password_323 #include <m_ctype.h> #include "my_md5.h" #include "sha1.h" @@ -45,15 +62,52 @@ C_MODE_END */ String my_empty_string("",default_charset_info); +/* + For the Items which have only val_str_ascii() method + and don't have their own "native" val_str(), + we provide a "wrapper" method to convert from ASCII + to Item character set when it's necessary. + Conversion happens only in case of "tricky" Item character set (e.g. UCS2). + Normally conversion does not happen, and val_str_ascii() is immediately + returned instead. +*/ +String *Item_str_func::val_str_from_val_str_ascii(String *str, String *str2) +{ + DBUG_ASSERT(fixed == 1); + + if (!(collation.collation->state & MY_CS_NONASCII)) + { + String *res= val_str_ascii(str); + if (res) + res->set_charset(collation.collation); + return res; + } + + DBUG_ASSERT(str != str2); + + uint errors; + String *res= val_str_ascii(str); + if (!res) + return 0; + + if ((null_value= str2->copy(res->ptr(), res->length(), + &my_charset_latin1, collation.collation, + &errors))) + return 0; + + return str2; +} + + /* Convert an array of bytes to a hexadecimal representation. Used to generate a hexadecimal representation of a message digest. */ -static void array_to_hex(char *to, const char *str, uint len) +static void array_to_hex(char *to, const unsigned char *str, uint len) { - const char *str_end= str + len; + const unsigned char *str_end= str + len; for (; str != str_end; ++str) { *to++= _dig_vec_lower[((uchar) *str) >> 4]; @@ -116,7 +170,7 @@ longlong Item_str_func::val_int() } -String *Item_func_md5::val_str(String *str) +String *Item_func_md5::val_str_ascii(String *str) { DBUG_ASSERT(fixed == 1); String * sptr= args[0]->val_str(str); @@ -132,7 +186,7 @@ String *Item_func_md5::val_str(String *str) null_value=1; return 0; } - array_to_hex((char *) str->ptr(), (const char*) digest, 16); + array_to_hex((char *) str->ptr(), digest, 16); str->length((uint) 32); return str; } @@ -143,7 +197,6 @@ String *Item_func_md5::val_str(String *str) void Item_func_md5::fix_length_and_dec() { - max_length=32; /* The MD5() function treats its parameter as being a case sensitive. Thus we set binary collation on it so different instances of MD5() will be @@ -152,10 +205,11 @@ void Item_func_md5::fix_length_and_dec() args[0]->collation.set( get_charset_by_csname(args[0]->collation.collation->csname, MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); + fix_length_and_charset(32, default_charset()); } -String *Item_func_sha::val_str(String *str) +String *Item_func_sha::val_str_ascii(String *str) { DBUG_ASSERT(fixed == 1); String * sptr= args[0]->val_str(str); @@ -173,7 +227,7 @@ String *Item_func_sha::val_str(String *str) if (!( str->alloc(SHA1_HASH_SIZE*2) || (mysql_sha1_result(&context,digest)))) { - array_to_hex((char *) str->ptr(), (const char*) digest, SHA1_HASH_SIZE); + array_to_hex((char *) str->ptr(), digest, SHA1_HASH_SIZE); str->length((uint) SHA1_HASH_SIZE*2); null_value=0; return str; @@ -185,7 +239,6 @@ String *Item_func_sha::val_str(String *str) void Item_func_sha::fix_length_and_dec() { - max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash /* The SHA() function treats its parameter as being a case sensitive. Thus we set binary collation on it so different instances of MD5() will be @@ -194,8 +247,148 @@ void Item_func_sha::fix_length_and_dec() args[0]->collation.set( get_charset_by_csname(args[0]->collation.collation->csname, MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); + // size of hex representation of hash + fix_length_and_charset(SHA1_HASH_SIZE * 2, default_charset()); } +String *Item_func_sha2::val_str_ascii(String *str) +{ + DBUG_ASSERT(fixed == 1); +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) + unsigned char digest_buf[SHA512_DIGEST_LENGTH]; + String *input_string; + unsigned char *input_ptr; + size_t input_len; + uint digest_length= 0; + + str->set_charset(&my_charset_bin); + + input_string= args[0]->val_str(str); + if (input_string == NULL) + { + null_value= TRUE; + return (String *) NULL; + } + + null_value= args[0]->null_value; + if (null_value) + return (String *) NULL; + + input_ptr= (unsigned char *) input_string->ptr(); + input_len= input_string->length(); + + switch ((uint) args[1]->val_int()) { +#ifndef OPENSSL_NO_SHA512 + case 512: + digest_length= SHA512_DIGEST_LENGTH; + (void) SHA512(input_ptr, input_len, digest_buf); + break; + case 384: + digest_length= SHA384_DIGEST_LENGTH; + (void) SHA384(input_ptr, input_len, digest_buf); + break; +#endif +#ifndef OPENSSL_NO_SHA256 + case 224: + digest_length= SHA224_DIGEST_LENGTH; + (void) SHA224(input_ptr, input_len, digest_buf); + break; + case 256: + case 0: // SHA-256 is the default + digest_length= SHA256_DIGEST_LENGTH; + (void) SHA256(input_ptr, input_len, digest_buf); + break; +#endif + default: + if (!args[1]->const_item()) + push_warning_printf(current_thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WRONG_PARAMETERS_TO_NATIVE_FCT, + ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2"); + null_value= TRUE; + return NULL; + } + + /* + Since we're subverting the usual String methods, we must make sure that + the destination has space for the bytes we're about to write. + */ + str->realloc((uint) digest_length*2 + 1); /* Each byte as two nybbles */ + + /* Convert the large number to a string-hex representation. */ + array_to_hex((char *) str->ptr(), digest_buf, digest_length); + + /* We poked raw bytes in. We must inform the the String of its length. */ + str->length((uint) digest_length*2); /* Each byte as two nybbles */ + + null_value= FALSE; + return str; + +#else + push_warning_printf(current_thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_FEATURE_DISABLED, + ER(ER_FEATURE_DISABLED), + "sha2", "--with-ssl"); + null_value= TRUE; + return (String *) NULL; +#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */ +} + + +void Item_func_sha2::fix_length_and_dec() +{ + maybe_null = 1; + max_length = 0; + +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) + int sha_variant= args[1]->const_item() ? args[1]->val_int() : 512; + + switch (sha_variant) { +#ifndef OPENSSL_NO_SHA512 + case 512: + fix_length_and_charset(SHA512_DIGEST_LENGTH * 2, default_charset()); + break; + case 384: + fix_length_and_charset(SHA384_DIGEST_LENGTH * 2, default_charset()); + break; +#endif +#ifndef OPENSSL_NO_SHA256 + case 256: + case 0: // SHA-256 is the default + fix_length_and_charset(SHA256_DIGEST_LENGTH * 2, default_charset()); + break; + case 224: + fix_length_and_charset(SHA224_DIGEST_LENGTH * 2, default_charset()); + break; +#endif + default: + push_warning_printf(current_thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WRONG_PARAMETERS_TO_NATIVE_FCT, + ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2"); + } + + /* + The SHA2() function treats its parameter as being a case sensitive. + Thus we set binary collation on it so different instances of SHA2() + will be compared properly. + */ + + args[0]->collation.set( + get_charset_by_csname( + args[0]->collation.collation->csname, + MY_CS_BINSORT, + MYF(0)), + DERIVATION_COERCIBLE); +#else + push_warning_printf(current_thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_FEATURE_DISABLED, + ER(ER_FEATURE_DISABLED), + "sha2", "--with-ssl"); +#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */ +} /* Implementation of AES encryption routines */ @@ -416,27 +609,15 @@ null: void Item_func_concat::fix_length_and_dec() { - ulonglong max_result_length= 0; + ulonglong char_length= 0; - if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1)) + if (agg_arg_charsets_for_string_result(collation, args, arg_count)) return; for (uint i=0 ; i < arg_count ; i++) - { - if (args[i]->collation.collation->mbmaxlen != collation.collation->mbmaxlen) - max_result_length+= (args[i]->max_length / - args[i]->collation.collation->mbmaxlen) * - collation.collation->mbmaxlen; - else - max_result_length+= args[i]->max_length; - } + char_length+= args[i]->max_char_length(); - if (max_result_length >= MAX_BLOB_WIDTH) - { - max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; - } - max_length= (ulong) max_result_length; + fix_char_length_ulonglong(char_length); } /** @@ -453,7 +634,7 @@ void Item_func_concat::fix_length_and_dec() String *Item_func_des_encrypt::val_str(String *str) { DBUG_ASSERT(fixed == 1); -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) uint code= ER_WRONG_PARAMETERS_TO_PROCEDURE; DES_cblock ivec; struct st_des_keyblock keyblock; @@ -466,22 +647,21 @@ String *Item_func_des_encrypt::val_str(String *str) return 0; // ENCRYPT(NULL) == NULL if ((res_length=res->length()) == 0) return make_empty_result(); - if (arg_count == 1) { /* Protect against someone doing FLUSH DES_KEY_FILE */ - VOID(pthread_mutex_lock(&LOCK_des_key_file)); + mysql_mutex_lock(&LOCK_des_key_file); keyschedule= des_keyschedule[key_number=des_default_key]; - VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + mysql_mutex_unlock(&LOCK_des_key_file); } else if (args[1]->result_type() == INT_RESULT) { key_number= (uint) args[1]->val_int(); if (key_number > 9) goto error; - VOID(pthread_mutex_lock(&LOCK_des_key_file)); + mysql_mutex_lock(&LOCK_des_key_file); keyschedule= des_keyschedule[key_number]; - VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + mysql_mutex_unlock(&LOCK_des_key_file); } else { @@ -534,14 +714,14 @@ String *Item_func_des_encrypt::val_str(String *str) return &tmp_value; error: - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, code, ER(code), "des_encrypt"); #else - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED), - "des_encrypt","--with-openssl"); -#endif /* HAVE_OPENSSL */ + "des_encrypt", "--with-ssl"); +#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */ null_value=1; return 0; } @@ -550,7 +730,7 @@ error: String *Item_func_des_decrypt::val_str(String *str) { DBUG_ASSERT(fixed == 1); -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) uint code= ER_WRONG_PARAMETERS_TO_PROCEDURE; DES_cblock ivec; struct st_des_keyblock keyblock; @@ -572,9 +752,9 @@ String *Item_func_des_decrypt::val_str(String *str) key_number > 9) goto error; - VOID(pthread_mutex_lock(&LOCK_des_key_file)); + mysql_mutex_lock(&LOCK_des_key_file); keyschedule= des_keyschedule[key_number]; - VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + mysql_mutex_unlock(&LOCK_des_key_file); } else { @@ -612,15 +792,15 @@ String *Item_func_des_decrypt::val_str(String *str) return &tmp_value; error: - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, code, ER(code), "des_decrypt"); wrong_key: #else - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED), - "des_decrypt","--with-openssl"); -#endif /* HAVE_OPENSSL */ + "des_decrypt", "--with-ssl"); +#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */ null_value=1; return 0; } @@ -776,9 +956,9 @@ null: void Item_func_concat_ws::fix_length_and_dec() { - ulonglong max_result_length; + ulonglong char_length; - if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1)) + if (agg_arg_charsets_for_string_result(collation, args, arg_count)) return; /* @@ -786,16 +966,11 @@ void Item_func_concat_ws::fix_length_and_dec() it is done on parser level in sql_yacc.yy so, (arg_count - 2) is safe here. */ - max_result_length= (ulonglong) args[0]->max_length * (arg_count - 2); + char_length= (ulonglong) args[0]->max_char_length() * (arg_count - 2); for (uint i=1 ; i < arg_count ; i++) - max_result_length+=args[i]->max_length; + char_length+= args[i]->max_char_length(); - if (max_result_length >= MAX_BLOB_WIDTH) - { - max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; - } - max_length= (ulong) max_result_length; + fix_char_length_ulonglong(char_length); } @@ -849,8 +1024,9 @@ String *Item_func_reverse::val_str(String *str) void Item_func_reverse::fix_length_and_dec() { - collation.set(args[0]->collation); - max_length = args[0]->max_length; + agg_arg_charsets_for_string_result(collation, args, 1); + DBUG_ASSERT(collation.collation != NULL); + fix_char_length(args[0]->max_char_length()); } /** @@ -985,22 +1161,17 @@ null: void Item_func_replace::fix_length_and_dec() { - ulonglong max_result_length= args[0]->max_length; - int diff=(int) (args[2]->max_length - args[1]->max_length); - if (diff > 0 && args[1]->max_length) + ulonglong char_length= (ulonglong) args[0]->max_char_length(); + int diff=(int) (args[2]->max_char_length() - args[1]->max_char_length()); + if (diff > 0 && args[1]->max_char_length()) { // Calculate of maxreplaces - ulonglong max_substrs= max_result_length/args[1]->max_length; - max_result_length+= max_substrs * (uint) diff; - } - if (max_result_length >= MAX_BLOB_WIDTH) - { - max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; + ulonglong max_substrs= char_length / args[1]->max_char_length(); + char_length+= max_substrs * (uint) diff; } - max_length= (ulong) max_result_length; - - if (agg_arg_charsets(collation, args, 3, MY_COLL_CMP_CONV, 1)) + + if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 3)) return; + fix_char_length_ulonglong(char_length); } @@ -1069,19 +1240,14 @@ null: void Item_func_insert::fix_length_and_dec() { - ulonglong max_result_length; + ulonglong char_length; // Handle character set for args[0] and args[3]. - if (agg_arg_charsets(collation, &args[0], 2, MY_COLL_ALLOW_CONV, 3)) + if (agg_arg_charsets_for_string_result(collation, args, 2, 3)) return; - max_result_length= ((ulonglong) args[0]->max_length+ - (ulonglong) args[3]->max_length); - if (max_result_length >= MAX_BLOB_WIDTH) - { - max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; - } - max_length= (ulong) max_result_length; + char_length= ((ulonglong) args[0]->max_char_length() + + (ulonglong) args[3]->max_char_length()); + fix_char_length_ulonglong(char_length); } @@ -1120,18 +1286,20 @@ String *Item_str_conv::val_str(String *str) void Item_func_lcase::fix_length_and_dec() { - collation.set(args[0]->collation); + agg_arg_charsets_for_string_result(collation, args, 1); + DBUG_ASSERT(collation.collation != NULL); multiply= collation.collation->casedn_multiply; converter= collation.collation->cset->casedn; - max_length= args[0]->max_length * multiply; + fix_char_length_ulonglong((ulonglong) args[0]->max_char_length() * multiply); } void Item_func_ucase::fix_length_and_dec() { - collation.set(args[0]->collation); + agg_arg_charsets_for_string_result(collation, args, 1); + DBUG_ASSERT(collation.collation != NULL); multiply= collation.collation->caseup_multiply; converter= collation.collation->cset->caseup; - max_length= args[0]->max_length * multiply; + fix_char_length_ulonglong((ulonglong) args[0]->max_char_length() * multiply); } @@ -1161,21 +1329,23 @@ String *Item_func_left::val_str(String *str) void Item_str_func::left_right_max_length() { - max_length=args[0]->max_length; + uint32 char_length= args[0]->max_char_length(); if (args[1]->const_item()) { - int length=(int) args[1]->val_int()*collation.collation->mbmaxlen; + int length= (int) args[1]->val_int(); if (length <= 0) - max_length=0; + char_length=0; else - set_if_smaller(max_length,(uint) length); + set_if_smaller(char_length, (uint) length); } + fix_char_length(char_length); } void Item_func_left::fix_length_and_dec() { - collation.set(args[0]->collation); + agg_arg_charsets_for_string_result(collation, args, 1); + DBUG_ASSERT(collation.collation != NULL); left_right_max_length(); } @@ -1208,7 +1378,8 @@ String *Item_func_right::val_str(String *str) void Item_func_right::fix_length_and_dec() { - collation.set(args[0]->collation); + agg_arg_charsets_for_string_result(collation, args, 1); + DBUG_ASSERT(collation.collation != NULL); left_right_max_length(); } @@ -1264,7 +1435,8 @@ void Item_func_substr::fix_length_and_dec() { max_length=args[0]->max_length; - collation.set(args[0]->collation); + agg_arg_charsets_for_string_result(collation, args, 1); + DBUG_ASSERT(collation.collation != NULL); if (args[1]->const_item()) { int32 start= (int32) args[1]->val_int(); @@ -1287,10 +1459,9 @@ void Item_func_substr::fix_length_and_dec() void Item_func_substr_index::fix_length_and_dec() { - max_length= args[0]->max_length; - - if (agg_arg_charsets(collation, args, 2, MY_COLL_CMP_CONV, 1)) + if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 2)) return; + fix_char_length(args[0]->max_char_length()); } @@ -1616,10 +1787,10 @@ String *Item_func_trim::val_str(String *str) void Item_func_trim::fix_length_and_dec() { - max_length= args[0]->max_length; if (arg_count == 1) { - collation.set(args[0]->collation); + agg_arg_charsets_for_string_result(collation, args, 1); + DBUG_ASSERT(collation.collation != NULL); remove.set_charset(collation.collation); remove.set_ascii(" ",1); } @@ -1627,9 +1798,11 @@ void Item_func_trim::fix_length_and_dec() { // Handle character set for args[1] and args[0]. // Note that we pass args[1] as the first item, and args[0] as the second. - if (agg_arg_charsets(collation, &args[1], 2, MY_COLL_CMP_CONV, -1)) + if (agg_arg_charsets_for_string_result_with_comparison(collation, + &args[1], 2, -1)) return; } + fix_char_length(args[0]->max_char_length()); } void Item_func_trim::print(String *str, enum_query_type query_type) @@ -1652,7 +1825,7 @@ void Item_func_trim::print(String *str, enum_query_type query_type) /* Item_func_password */ -String *Item_func_password::val_str(String *str) +String *Item_func_password::val_str_ascii(String *str) { DBUG_ASSERT(fixed == 1); String *res= args[0]->val_str(str); @@ -1661,7 +1834,7 @@ String *Item_func_password::val_str(String *str) if (res->length() == 0) return make_empty_result(); my_make_scrambled_password(tmp_value, res->ptr(), res->length()); - str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, res->charset()); + str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, &my_charset_latin1); return str; } @@ -1676,7 +1849,7 @@ char *Item_func_password::alloc(THD *thd, const char *password, /* Item_func_old_password */ -String *Item_func_old_password::val_str(String *str) +String *Item_func_old_password::val_str_ascii(String *str) { DBUG_ASSERT(fixed == 1); String *res= args[0]->val_str(str); @@ -1685,7 +1858,7 @@ String *Item_func_old_password::val_str(String *str) if (res->length() == 0) return make_empty_result(); my_make_scrambled_password_323(tmp_value, res->ptr(), res->length()); - str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, res->charset()); + str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, &my_charset_latin1); return str; } @@ -1727,17 +1900,17 @@ String *Item_func_encrypt::val_str(String *str) return 0; salt_ptr= salt_str->c_ptr_safe(); } - pthread_mutex_lock(&LOCK_crypt); + mysql_mutex_lock(&LOCK_crypt); char *tmp= crypt(res->c_ptr_safe(),salt_ptr); if (!tmp) { - pthread_mutex_unlock(&LOCK_crypt); + mysql_mutex_unlock(&LOCK_crypt); null_value= 1; return 0; } str->set(tmp, (uint) strlen(tmp), &my_charset_bin); str->copy(); - pthread_mutex_unlock(&LOCK_crypt); + mysql_mutex_unlock(&LOCK_crypt); return str; #else null_value=1; @@ -1904,9 +2077,11 @@ bool Item_func_current_user::fix_fields(THD *thd, Item **ref) void Item_func_soundex::fix_length_and_dec() { - collation.set(args[0]->collation); - max_length=args[0]->max_length; - set_if_bigger(max_length, 4 * collation.collation->mbminlen); + uint32 char_length= args[0]->max_char_length(); + agg_arg_charsets_for_string_result(collation, args, 1); + DBUG_ASSERT(collation.collation != NULL); + set_if_bigger(char_length, 4); + fix_char_length(char_length); tmp_value.set_charset(collation.collation); } @@ -2063,18 +2238,34 @@ String *Item_func_soundex::val_str(String *str) const int FORMAT_MAX_DECIMALS= 30; -Item_func_format::Item_func_format(Item *org, Item *dec) -: Item_str_func(org, dec) + +MY_LOCALE *Item_func_format::get_locale(Item *item) { + DBUG_ASSERT(arg_count == 3); + String tmp, *locale_name= args[2]->val_str_ascii(&tmp); + MY_LOCALE *lc; + if (!locale_name || + !(lc= my_locale_by_name(locale_name->c_ptr_safe()))) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_UNKNOWN_LOCALE, + ER(ER_UNKNOWN_LOCALE), + locale_name ? locale_name->c_ptr_safe() : "NULL"); + lc= &my_locale_en_US; + } + return lc; } void Item_func_format::fix_length_and_dec() { - uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen; - uint max_sep_count= char_length/3 + (decimals ? 1 : 0) + /*sign*/1; + uint32 char_length= args[0]->max_char_length(); + uint32 max_sep_count= (char_length / 3) + (decimals ? 1 : 0) + /*sign*/1; collation.set(default_charset()); - max_length= (char_length + max_sep_count + decimals) * - collation.collation->mbmaxlen; + fix_char_length(char_length + max_sep_count + decimals); + if (arg_count == 3) + locale= args[2]->basic_const_item() ? get_locale(args[2]) : NULL; + else + locale= &my_locale_en_US; /* Two arguments */ } @@ -2084,15 +2275,14 @@ void Item_func_format::fix_length_and_dec() are stored in more than one byte */ -String *Item_func_format::val_str(String *str) +String *Item_func_format::val_str_ascii(String *str) { - uint32 length; uint32 str_length; /* Number of decimal digits */ int dec; /* Number of characters used to represent the decimals, including '.' */ uint32 dec_length; - int diff; + MY_LOCALE *lc; DBUG_ASSERT(fixed == 1); dec= (int) args[1]->val_int(); @@ -2102,6 +2292,8 @@ String *Item_func_format::val_str(String *str) return NULL; } + lc= locale ? locale : get_locale(args[2]); + dec= set_zone(dec, 0, FORMAT_MAX_DECIMALS); dec_length= dec ? dec+1 : 0; null_value=0; @@ -2116,8 +2308,6 @@ String *Item_func_format::val_str(String *str) my_decimal_round(E_DEC_FATAL_ERROR, res, dec, false, &rnd_dec); my_decimal2string(E_DEC_FATAL_ERROR, &rnd_dec, 0, 0, 0, str); str_length= str->length(); - if (rnd_dec.sign()) - str_length--; } else { @@ -2125,36 +2315,65 @@ String *Item_func_format::val_str(String *str) if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ nr= my_double_round(nr, (longlong) dec, FALSE, FALSE); - /* Here default_charset() is right as this is not an automatic conversion */ - str->set_real(nr, dec, default_charset()); - if (isnan(nr)) + str->set_real(nr, dec, &my_charset_numeric); + if (isnan(nr) || my_isinf(nr)) return str; str_length=str->length(); - if (nr < 0) - str_length--; // Don't count sign - } - /* We need this test to handle 'nan' values */ - if (str_length >= dec_length+4) - { - char *tmp,*pos; - length= str->length()+(diff=((int)(str_length- dec_length-1))/3); - str= copy_if_not_alloced(&tmp_str,str,length); - str->length(length); - tmp= (char*) str->ptr()+length - dec_length-1; - for (pos= (char*) str->ptr()+length-1; pos != tmp; pos--) - pos[0]= pos[-diff]; - while (diff) + } + /* We need this test to handle 'nan' and short values */ + if (lc->grouping[0] > 0 && + str_length >= dec_length + 1 + lc->grouping[0]) + { + /* We need space for ',' between each group of digits as well. */ + char buf[2 * FLOATING_POINT_BUFFER]; + int count; + const char *grouping= lc->grouping; + char sign_length= *str->ptr() == '-' ? 1 : 0; + const char *src= str->ptr() + str_length - dec_length - 1; + const char *src_begin= str->ptr() + sign_length; + char *dst= buf + sizeof(buf); + + /* Put the fractional part */ + if (dec) { - *pos= *(pos - diff); - pos--; - *pos= *(pos - diff); - pos--; - *pos= *(pos - diff); - pos--; - pos[0]=','; - pos--; - diff--; + dst-= (dec + 1); + *dst= lc->decimal_point; + memcpy(dst + 1, src + 2, dec); } + + /* Put the integer part with grouping */ + for (count= *grouping; src >= src_begin; count--) + { + /* + When *grouping==0x80 (which means "end of grouping") + count will be initialized to -1 and + we'll never get into this "if" anymore. + */ + if (count == 0) + { + *--dst= lc->thousand_sep; + if (grouping[1]) + grouping++; + count= *grouping; + } + DBUG_ASSERT(dst > buf); + *--dst= *src--; + } + + if (sign_length) /* Put '-' */ + *--dst= *str->ptr(); + + /* Put the rest of the integer part without grouping */ + str->copy(dst, buf + sizeof(buf) - dst, &my_charset_latin1); + } + else if (dec_length && lc->decimal_point != '.') + { + /* + For short values without thousands (<1000) + replace decimal point to localized value. + */ + DBUG_ASSERT(dec_length <= str_length); + ((char*) str->ptr())[str_length - dec_length]= lc->decimal_point; } return str; } @@ -2166,22 +2385,28 @@ void Item_func_format::print(String *str, enum_query_type query_type) args[0]->print(str, query_type); str->append(','); args[1]->print(str, query_type); + if(arg_count > 2) + { + str->append(','); + args[2]->print(str,query_type); + } str->append(')'); } void Item_func_elt::fix_length_and_dec() { - max_length=0; + uint32 char_length= 0; decimals=0; - if (agg_arg_charsets(collation, args+1, arg_count-1, MY_COLL_ALLOW_CONV, 1)) + if (agg_arg_charsets_for_string_result(collation, args + 1, arg_count - 1)) return; for (uint i= 1 ; i < arg_count ; i++) { - set_if_bigger(max_length,args[i]->max_length); + set_if_bigger(char_length, args[i]->max_char_length()); set_if_bigger(decimals,args[i]->decimals); } + fix_char_length(char_length); maybe_null=1; // NULL if wrong first arg } @@ -2239,14 +2464,14 @@ void Item_func_make_set::split_sum_func(THD *thd, Item **ref_pointer_array, void Item_func_make_set::fix_length_and_dec() { - max_length=arg_count-1; + uint32 char_length= arg_count - 1; /* Separators */ - if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1)) + if (agg_arg_charsets_for_string_result(collation, args, arg_count)) return; for (uint i=0 ; i < arg_count ; i++) - max_length+=args[i]->max_length; - + char_length+= args[i]->max_char_length(); + fix_char_length(char_length); used_tables_cache|= item->used_tables(); not_null_tables_cache&= item->not_null_tables(); const_item_cache&= item->const_item(); @@ -2318,7 +2543,7 @@ String *Item_func_make_set::val_str(String *str) Item *Item_func_make_set::transform(Item_transformer transformer, uchar *arg) { - DBUG_ASSERT(!current_thd->is_stmt_prepare()); + DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); Item *new_item= item->transform(transformer, arg); if (!new_item) @@ -2359,17 +2584,27 @@ String *Item_func_char::val_str(String *str) int32 num=(int32) args[i]->val_int(); if (!args[i]->null_value) { - char char_num= (char) num; - if (num&0xFF000000L) { - str->append((char)(num>>24)); - goto b2; - } else if (num&0xFF0000L) { - b2: str->append((char)(num>>16)); - goto b1; - } else if (num&0xFF00L) { - b1: str->append((char)(num>>8)); + char tmp[4]; + if (num & 0xFF000000L) + { + mi_int4store(tmp, num); + str->append(tmp, 4, &my_charset_bin); + } + else if (num & 0xFF0000L) + { + mi_int3store(tmp, num); + str->append(tmp, 3, &my_charset_bin); + } + else if (num & 0xFF00L) + { + mi_int2store(tmp, num); + str->append(tmp, 2, &my_charset_bin); + } + else + { + tmp[0]= (char) num; + str->append(tmp, 1, &my_charset_bin); } - str->append(&char_num, 1); } } str->realloc(str->length()); // Add end 0 (for Purify) @@ -2401,7 +2636,8 @@ inline String* alloc_buffer(String *res,String *str,String *tmp_value, void Item_func_repeat::fix_length_and_dec() { - collation.set(args[0]->collation); + agg_arg_charsets_for_string_result(collation, args, 1); + DBUG_ASSERT(collation.collation != NULL); if (args[1]->const_item()) { /* must be longlong to avoid truncation */ @@ -2412,13 +2648,8 @@ void Item_func_repeat::fix_length_and_dec() if (count > INT_MAX32) count= INT_MAX32; - ulonglong max_result_length= (ulonglong) args[0]->max_length * count; - if (max_result_length >= MAX_BLOB_WIDTH) - { - max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; - } - max_length= (ulong) max_result_length; + ulonglong char_length= (ulonglong) args[0]->max_char_length() * count; + fix_char_length_ulonglong(char_length); } else { @@ -2485,30 +2716,17 @@ err: void Item_func_rpad::fix_length_and_dec() { // Handle character set for args[0] and args[2]. - if (agg_arg_charsets(collation, &args[0], 2, MY_COLL_ALLOW_CONV, 2)) + if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2)) return; if (args[1]->const_item()) { - ulonglong length= 0; - - if (collation.collation->mbmaxlen > 0) - { - ulonglong temp= (ulonglong) args[1]->val_int(); - - /* Assumes that the maximum length of a String is < INT_MAX32. */ - /* Set here so that rest of code sees out-of-bound value as such. */ - if (temp > INT_MAX32) - temp = INT_MAX32; - - length= temp * collation.collation->mbmaxlen; - } - - if (length >= MAX_BLOB_WIDTH) - { - length= MAX_BLOB_WIDTH; - maybe_null= 1; - } - max_length= (ulong) length; + ulonglong char_length= (ulonglong) args[1]->val_int(); + DBUG_ASSERT(collation.collation->mbmaxlen > 0); + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if (char_length > INT_MAX32) + char_length= INT_MAX32; + fix_char_length_ulonglong(char_length); } else { @@ -2601,31 +2819,18 @@ String *Item_func_rpad::val_str(String *str) void Item_func_lpad::fix_length_and_dec() { // Handle character set for args[0] and args[2]. - if (agg_arg_charsets(collation, &args[0], 2, MY_COLL_ALLOW_CONV, 2)) + if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2)) return; if (args[1]->const_item()) { - ulonglong length= 0; - - if (collation.collation->mbmaxlen > 0) - { - ulonglong temp= (ulonglong) args[1]->val_int(); - - /* Assumes that the maximum length of a String is < INT_MAX32. */ - /* Set here so that rest of code sees out-of-bound value as such. */ - if (temp > INT_MAX32) - temp= INT_MAX32; - - length= temp * collation.collation->mbmaxlen; - } - - if (length >= MAX_BLOB_WIDTH) - { - length= MAX_BLOB_WIDTH; - maybe_null= 1; - } - max_length= (ulong) length; + ulonglong char_length= (ulonglong) args[1]->val_int(); + DBUG_ASSERT(collation.collation->mbmaxlen > 0); + /* Assumes that the maximum length of a String is < INT_MAX32. */ + /* Set here so that rest of code sees out-of-bound value as such. */ + if (char_length > INT_MAX32) + char_length= INT_MAX32; + fix_char_length_ulonglong(char_length); } else { @@ -2752,9 +2957,12 @@ String *Item_func_conv::val_str(String *str) from_base, &endptr, &err); } - ptr= longlong2str(dec, ans, to_base); - if (str->copy(ans, (uint32) (ptr-ans), default_charset())) - return make_empty_result(); + if (!(ptr= longlong2str(dec, ans, to_base)) || + str->copy(ans, (uint32) (ptr - ans), default_charset())) + { + null_value= 1; + return NULL; + } return str; } @@ -2779,7 +2987,7 @@ String *Item_func_conv_charset::val_str(String *str) void Item_func_conv_charset::fix_length_and_dec() { collation.set(conv_charset, DERIVATION_IMPLICIT); - max_length = args[0]->max_length*conv_charset->mbmaxlen; + fix_char_length(args[0]->max_char_length()); } void Item_func_conv_charset::print(String *str, enum_query_type query_type) @@ -2869,7 +3077,7 @@ String *Item_func_charset::val_str(String *str) DBUG_ASSERT(fixed == 1); uint dummy_errors; - CHARSET_INFO *cs= args[0]->collation.collation; + CHARSET_INFO *cs= args[0]->charset_for_protocol(); null_value= 0; str->copy(cs->csname, (uint) strlen(cs->csname), &my_charset_latin1, collation.collation, &dummy_errors); @@ -2880,7 +3088,7 @@ String *Item_func_collation::val_str(String *str) { DBUG_ASSERT(fixed == 1); uint dummy_errors; - CHARSET_INFO *cs= args[0]->collation.collation; + CHARSET_INFO *cs= args[0]->charset_for_protocol(); null_value= 0; str->copy(cs->name, (uint) strlen(cs->name), @@ -2889,7 +3097,7 @@ String *Item_func_collation::val_str(String *str) } -String *Item_func_hex::val_str(String *str) +String *Item_func_hex::val_str_ascii(String *str) { String *res; DBUG_ASSERT(fixed == 1); @@ -2913,9 +3121,11 @@ String *Item_func_hex::val_str(String *str) if ((null_value= args[0]->null_value)) return 0; - ptr= longlong2str(dec,ans,16); - if (str->copy(ans,(uint32) (ptr-ans),default_charset())) - return make_empty_result(); // End of memory + + if (!(ptr= longlong2str(dec, ans, 16)) || + str->copy(ans,(uint32) (ptr - ans), + &my_charset_numeric)) + return make_empty_result(); // End of memory return str; } @@ -2928,6 +3138,7 @@ String *Item_func_hex::val_str(String *str) } null_value=0; tmp_value.length(res->length()*2); + tmp_value.set_charset(&my_charset_latin1); octet2hex((char*) tmp_value.ptr(), res->ptr(), res->length()); return &tmp_value; @@ -2975,6 +3186,41 @@ String *Item_func_unhex::val_str(String *str) } +#ifndef DBUG_OFF +String *Item_func_like_range::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + longlong nbytes= args[1]->val_int(); + String *res= args[0]->val_str(str); + size_t min_len, max_len; + CHARSET_INFO *cs= collation.collation; + + if (!res || args[0]->null_value || args[1]->null_value || + nbytes < 0 || nbytes > MAX_BLOB_WIDTH || + min_str.alloc(nbytes) || max_str.alloc(nbytes)) + goto err; + null_value=0; + + if (cs->coll->like_range(cs, res->ptr(), res->length(), + '\\', '_', '%', nbytes, + (char*) min_str.ptr(), (char*) max_str.ptr(), + &min_len, &max_len)) + goto err; + + min_str.set_charset(collation.collation); + max_str.set_charset(collation.collation); + min_str.length(min_len); + max_str.length(max_len); + + return is_min ? &min_str : &max_str; + +err: + null_value= 1; + return 0; +} +#endif + + void Item_func_binary::print(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN("cast(")); @@ -3008,7 +3254,7 @@ String *Item_load_file::val_str(String *str) if (!is_secure_file_path(path)) goto err; - if (!my_stat(path, &stat_info, MYF(0))) + if (!mysql_file_stat(key_file_loadfile, path, &stat_info, MYF(0))) goto err; if (!(stat_info.st_mode & S_IROTH)) @@ -3026,15 +3272,17 @@ String *Item_load_file::val_str(String *str) } if (tmp_value.alloc(stat_info.st_size)) goto err; - if ((file = my_open(file_name->ptr(), O_RDONLY, MYF(0))) < 0) + if ((file= mysql_file_open(key_file_loadfile, + file_name->ptr(), O_RDONLY, MYF(0))) < 0) goto err; - if (my_read(file, (uchar*) tmp_value.ptr(), stat_info.st_size, MYF(MY_NABP))) + if (mysql_file_read(file, (uchar*) tmp_value.ptr(), stat_info.st_size, + MYF(MY_NABP))) { - my_close(file, MYF(0)); + mysql_file_close(file, MYF(0)); goto err; } tmp_value.length(stat_info.st_size); - my_close(file, MYF(0)); + mysql_file_close(file, MYF(0)); null_value = 0; DBUG_RETURN(&tmp_value); @@ -3131,13 +3379,13 @@ String* Item_func_export_set::val_str(String* str) void Item_func_export_set::fix_length_and_dec() { - uint length=max(args[1]->max_length,args[2]->max_length); - uint sep_length=(arg_count > 3 ? args[3]->max_length : 1); - max_length=length*64+sep_length*63; + uint32 length= max(args[1]->max_char_length(), args[2]->max_char_length()); + uint32 sep_length= (arg_count > 3 ? args[3]->max_char_length() : 1); - if (agg_arg_charsets(collation, args+1, min(4,arg_count)-1, - MY_COLL_ALLOW_CONV, 1)) + if (agg_arg_charsets_for_string_result(collation, + args + 1, min(4, arg_count) - 1)) return; + fix_char_length(length * 64 + sep_length * 63); } String* Item_func_inet_ntoa::val_str(String* str) @@ -3174,11 +3422,11 @@ String* Item_func_inet_ntoa::val_str(String* str) num[0]=(char) n1+'0'; num[1]=(char) n2+'0'; num[2]=(char) c+'0'; - uint length=(n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero - - (void) str->append(num+4-length,length); + uint length= (n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero + uint dot_length= (p <= buf) ? 1 : 0; + (void) str->append(num + 4 - length, length - dot_length, + &my_charset_latin1); } - str->length(str->length()-1); // Remove last '.'; return str; } @@ -3355,7 +3603,7 @@ longlong Item_func_uncompressed_length::val_int() */ if (res->length() <= 4) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_ZLIB_Z_DATA_ERROR, ER(ER_ZLIB_Z_DATA_ERROR)); null_value= 1; @@ -3432,7 +3680,7 @@ String *Item_func_compress::val_str(String *str) (const Bytef*)res->ptr(), res->length())) != Z_OK) { code= err==Z_MEM_ERROR ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_BUF_ERROR; - push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,code,ER(code)); + push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,code,ER(code)); null_value= 1; return 0; } @@ -3470,7 +3718,7 @@ String *Item_func_uncompress::val_str(String *str) /* If length is less than 4 bytes, data is corrupt */ if (res->length() <= 4) { - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, ER_ZLIB_Z_DATA_ERROR, ER(ER_ZLIB_Z_DATA_ERROR)); goto err; @@ -3480,7 +3728,7 @@ String *Item_func_uncompress::val_str(String *str) new_size= uint4korr(res->ptr()) & 0x3FFFFFFF; if (new_size > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, ER_TOO_BIG_FOR_UNCOMPRESS, ER(ER_TOO_BIG_FOR_UNCOMPRESS), static_cast<int>(current_thd->variables. @@ -3499,7 +3747,7 @@ String *Item_func_uncompress::val_str(String *str) code= ((err == Z_BUF_ERROR) ? ER_ZLIB_Z_BUF_ERROR : ((err == Z_MEM_ERROR) ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_DATA_ERROR)); - push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,code,ER(code)); + push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,code,ER(code)); err: null_value= 1; @@ -3553,7 +3801,7 @@ String *Item_func_uuid::val_str(String *str) char *s; THD *thd= current_thd; - pthread_mutex_lock(&LOCK_uuid_generator); + mysql_mutex_lock(&LOCK_uuid_generator); if (! uuid_time) /* first UUID() call. initializing data */ { ulong tmp=sql_rnd_with_mutex(); @@ -3643,7 +3891,7 @@ String *Item_func_uuid::val_str(String *str) } uuid_time=tv; - pthread_mutex_unlock(&LOCK_uuid_generator); + mysql_mutex_unlock(&LOCK_uuid_generator); uint32 time_low= (uint32) (tv & 0xFFFFFFFF); uint16 time_mid= (uint16) ((tv >> 32) & 0xFFFF); |