From: Martin Hansson Date: January 13 2011 8:20am Subject: bzr commit into mysql-trunk branch (martin.hansson:3494) Bug#58165 List-Archive: http://lists.mysql.com/commits/128587 X-Bug: 58165 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1589401353==" --===============1589401353== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///data0/martin/bzrroot/bug58165/t/ based on revid:olav.sandstaa@stripped 3494 Martin Hansson 2011-01-13 [merge] Merge of fix for Bug#58165. modified: mysql-test/r/func_str.result mysql-test/t/func_str.test sql/item_strfunc.cc sql/item_strfunc.h sql/sql_string.cc sql/sql_string.h === modified file 'mysql-test/r/func_str.result' --- a/mysql-test/r/func_str.result 2010-12-14 16:29:15 +0000 +++ b/mysql-test/r/func_str.result 2011-01-13 08:19:52 +0000 @@ -2615,6 +2615,22 @@ CONVERT(('' IN (REVERSE(CAST(('') AS DEC 1 Warnings: Warning 1292 Truncated incorrect DECIMAL value: '' +# +# Bug#58165: "my_empty_string" gets modified and causes LOAD DATA to fail +# and other crashes +# +CREATE TABLE t1 ( a TEXT ); +SELECT 'aaaaaaaaaaaaaa' INTO OUTFILE 'bug58165.txt'; +SELECT insert( substring_index( 'a', 'a', 'b' ), 1, 0, 'x' ); +insert( substring_index( 'a', 'a', 'b' ), 1, 0, 'x' ) +x +Warnings: +Warning 1292 Truncated incorrect INTEGER value: 'b' +LOAD DATA INFILE 'bug58165.txt' INTO TABLE t1; +SELECT * FROM t1; +a +aaaaaaaaaaaaaa +DROP TABLE t1; End of 5.1 tests Start of 5.4 tests SELECT format(12345678901234567890.123, 3); === modified file 'mysql-test/t/func_str.test' --- a/mysql-test/t/func_str.test 2010-12-14 16:29:15 +0000 +++ b/mysql-test/t/func_str.test 2011-01-13 08:19:52 +0000 @@ -1370,6 +1370,17 @@ DROP TABLE t1; SELECT '1' IN ('1', SUBSTRING(-9223372036854775809, 1)); SELECT CONVERT(('' IN (REVERSE(CAST(('') AS DECIMAL)), '')), CHAR(3)); +--echo # +--echo # Bug#58165: "my_empty_string" gets modified and causes LOAD DATA to fail +--echo # and other crashes +--echo # +CREATE TABLE t1 ( a TEXT ); +SELECT 'aaaaaaaaaaaaaa' INTO OUTFILE 'bug58165.txt'; +SELECT insert( substring_index( 'a', 'a', 'b' ), 1, 0, 'x' ); +LOAD DATA INFILE 'bug58165.txt' INTO TABLE t1; +SELECT * FROM t1; +DROP TABLE t1; + --echo End of 5.1 tests --echo Start of 5.4 tests === modified file 'sql/item_strfunc.cc' --- a/sql/item_strfunc.cc 2010-12-20 10:28:06 +0000 +++ b/sql/item_strfunc.cc 2011-01-13 08:19:52 +0000 @@ -57,6 +57,9 @@ C_MODE_START #include "../mysys/my_static.h" // For soundex_map C_MODE_END +/** + @todo Remove this. It is not safe to use a shared String object. + */ String my_empty_string("",default_charset_info); /* @@ -739,7 +742,7 @@ String *Item_func_des_encrypt::val_str(S if ((null_value= args[0]->null_value)) return 0; // ENCRYPT(NULL) == NULL if ((res_length=res->length()) == 0) - return &my_empty_string; + return make_empty_result(); if (arg_count == 1) { /* Protect against someone doing FLUSH DES_KEY_FILE */ @@ -929,7 +932,7 @@ String *Item_func_concat_ws::val_str(Str } if (i == arg_count) - return &my_empty_string; + return make_empty_result(); for (i++; i < arg_count ; i++) { @@ -1075,7 +1078,7 @@ String *Item_func_reverse::val_str(Strin return 0; /* An empty string is a special case as the string pointer may be null */ if (!res->length()) - return &my_empty_string; + return make_empty_result(); if (tmp_value.alloced_length() < res->length() && tmp_value.realloc(res->length())) { @@ -1408,8 +1411,7 @@ String *Item_func_left::val_str(String * /* if "unsigned_flag" is set, we have a *huge* positive number. */ if ((length <= 0) && (!args[1]->unsigned_flag)) - return &my_empty_string; - + return make_empty_result(); if ((res->length() <= (ulonglong) length) || (res->length() <= (char_pos= res->charpos((int) length)))) return res; @@ -1454,7 +1456,7 @@ String *Item_func_right::val_str(String /* if "unsigned_flag" is set, we have a *huge* positive number. */ if ((length <= 0) && (!args[1]->unsigned_flag)) - return &my_empty_string; /* purecov: inspected */ + return make_empty_result(); /* purecov: inspected */ if (res->length() <= (ulonglong) length) return res; /* purecov: inspected */ @@ -1494,7 +1496,7 @@ String *Item_func_substr::val_str(String /* Negative or zero length, will return empty string. */ if ((arg_count == 3) && (length <= 0) && (length == 0 || !args[2]->unsigned_flag)) - return &my_empty_string; + return make_empty_result(); /* 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. */ @@ -1505,12 +1507,12 @@ String *Item_func_substr::val_str(String /* Assumes that the maximum length of a String is < INT_MAX32. */ if ((!args[1]->unsigned_flag && (start < INT_MIN32 || start > INT_MAX32)) || (args[1]->unsigned_flag && ((ulonglong) start > INT_MAX32))) - return &my_empty_string; + return make_empty_result(); start= ((start < 0) ? res->numchars() + start : start - 1); start= res->charpos((int) start); if ((start < 0) || ((uint) start + 1 > res->length())) - return &my_empty_string; + return make_empty_result(); length= res->charpos((int) length, (uint32) start); tmp_length= res->length() - start; @@ -1573,7 +1575,7 @@ String *Item_func_substr_index::val_str( null_value=0; uint delimiter_length= delimiter->length(); if (!res->length() || !delimiter_length || !count) - return &my_empty_string; // Wrong parameters + return make_empty_result(); // Wrong parameters res->set_charset(collation.collation); @@ -1923,7 +1925,7 @@ String *Item_func_password::val_str_asci if ((null_value=args[0]->null_value)) return 0; if (res->length() == 0) - return &my_empty_string; + return make_empty_result(); my_make_scrambled_password(tmp_value, res->ptr(), res->length()); str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, &my_charset_latin1); return str; @@ -1947,7 +1949,7 @@ String *Item_func_old_password::val_str_ if ((null_value=args[0]->null_value)) return 0; if (res->length() == 0) - return &my_empty_string; + return make_empty_result(); my_make_scrambled_password_323(tmp_value, res->ptr(), res->length()); str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, &my_charset_latin1); return str; @@ -1975,8 +1977,7 @@ String *Item_func_encrypt::val_str(Strin if ((null_value=args[0]->null_value)) return 0; if (res->length() == 0) - return &my_empty_string; - + return make_empty_result(); if (arg_count == 1) { // generate random salt time_t timestamp=current_thd->query_start(); @@ -2238,7 +2239,7 @@ String *Item_func_soundex::val_str(Strin for ( ; ; ) /* Skip pre-space */ { if ((rc= cs->cset->mb_wc(cs, &wc, (uchar*) from, (uchar*) end)) <= 0) - return &my_empty_string; /* EOL or invalid byte sequence */ + return make_empty_result(); /* EOL or invalid byte sequence */ if (rc == 1 && cs->ctype) { @@ -2263,7 +2264,7 @@ String *Item_func_soundex::val_str(Strin { /* Extra safety - should not really happen */ DBUG_ASSERT(false); - return &my_empty_string; + return make_empty_result(); } to+= rc; break; @@ -2604,7 +2605,7 @@ String *Item_func_make_set::val_str(Stri else { if (tmp_str.copy(*res)) // Don't use 'str' - return &my_empty_string; + return make_empty_result(); result= &tmp_str; } } @@ -2614,11 +2615,11 @@ String *Item_func_make_set::val_str(Stri { // Copy data to tmp_str if (tmp_str.alloc(result->length()+res->length()+1) || tmp_str.copy(*result)) - return &my_empty_string; + return make_empty_result(); result= &tmp_str; } if (tmp_str.append(STRING_WITH_LEN(","), &my_charset_bin) || tmp_str.append(*res)) - return &my_empty_string; + return make_empty_result(); } } } @@ -2763,7 +2764,7 @@ String *Item_func_repeat::val_str(String null_value= 0; if (count <= 0 && (count == 0 || !args[1]->unsigned_flag)) - return &my_empty_string; + return make_empty_result(); /* Assumes that the maximum length of a String is < INT_MAX32. */ /* Bounds check on count: If this is triggered, we will error. */ @@ -3045,7 +3046,7 @@ String *Item_func_conv::val_str(String * ptr= longlong2str(dec, ans, to_base); if (str->copy(ans, (uint32) (ptr-ans), default_charset())) - return &my_empty_string; + return make_empty_result(); return str; } @@ -3264,7 +3265,7 @@ String *Item_func_hex::val_str_ascii(Str return 0; ptr= longlong2str(dec,ans,16); if (str->copy(ans,(uint32) (ptr-ans), &my_charset_numeric)) - return &my_empty_string; // End of memory + return make_empty_result(); // End of memory return str; } === modified file 'sql/item_strfunc.h' --- a/sql/item_strfunc.h 2010-12-02 13:44:21 +0000 +++ b/sql/item_strfunc.h 2011-01-13 08:19:52 +0000 @@ -27,6 +27,16 @@ class MY_LOCALE; class Item_str_func :public Item_func { +protected: + /** + Sets the result value of the function an empty string, using the current + character set. No memory is allocated. + @retval A pointer to the str_value member. + */ + String *make_empty_result() { + str_value.set("", 0, collation.collation); + return &str_value; + } public: Item_str_func() :Item_func() { decimals=NOT_FIXED_DEC; } Item_str_func(Item *a) :Item_func(a) {decimals=NOT_FIXED_DEC; } === modified file 'sql/sql_string.cc' --- a/sql/sql_string.cc 2010-12-14 13:26:35 +0000 +++ b/sql/sql_string.cc 2011-01-13 08:19:52 +0000 @@ -51,11 +51,33 @@ bool String::real_alloc(uint32 length) } -/* -** Check that string is big enough. Set string[alloc_length] to 0 -** (for C functions) -*/ +/** + Allocates a new buffer on the heap for this String. + + - If the String's internal buffer is privately owned and heap allocated, + one of the following is performed. + + - If the requested length is greater than what fits in the buffer, a new + buffer is allocated, data moved and the old buffer freed. + + - If the requested length is less or equal to what fits in the buffer, a + null character is inserted at the appropriate position. + - If the String does not keep a private buffer on the heap, such a buffer + will be allocated and the string copied accoring to its length, as found + in String::length(). + + For C compatibility, the new string buffer is null terminated. + + @param alloc_length The requested string size in characters, excluding any + null terminator. + + @retval false Either the copy operation is complete or, if the size of the + new buffer is smaller than the currently allocated buffer (if one exists), + no allocation occured. + + @retval true An error occured when attempting to allocate memory. +*/ bool String::realloc(uint32 alloc_length) { uint32 len=ALIGN_SIZE(alloc_length+1); @@ -128,6 +150,17 @@ bool String::copy() return FALSE; } +/** + Copies the internal buffer from str. If this String has a private heap + allocated buffer where new data does not fit, a new buffer is allocated + before copying and the old buffer freed. Character set information is also + copied. + + @param str The string whose internal buffer is to be copied. + + @retval false Success. + @retval true Memory allocation failed. +*/ bool String::copy(const String &str) { if (alloc(str.str_length)) === modified file 'sql/sql_string.h' --- a/sql/sql_string.h 2010-10-27 14:46:44 +0000 +++ b/sql/sql_string.h 2011-01-13 08:19:52 +0000 @@ -148,6 +148,16 @@ public: Alloced_length=0; str_charset=str.str_charset; } + + + /** + Points the internal buffer to the supplied one. The old buffer is freed. + @param str Pointer to the new buffer. + @param arg_length Length of the new buffer in characters, excluding any + null character. + @param cs Character set to use for interpreting string data. + @note The new buffer will not be null terminated. + */ inline void set(char *str,uint32 arg_length, CHARSET_INFO *cs) { free(); --===============1589401353== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/martin.hansson@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: martin.hansson@stripped\ # n7mg2swpu1xb5fjp # target_branch: file:///data0/martin/bzrroot/bug58165/t/ # testament_sha1: c50059ec63068969e42564d2da1802070b04075e # timestamp: 2011-01-13 09:20:38 +0100 # source_branch: file:///data0/martin/bzrroot/bug58165/5.5/ # base_revision_id: olav.sandstaa@stripped\ # xtc6rhbo1g9iaiqm # # Begin bundle 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 3G4eLPW5IPU/wLuSKcKEgaH/g4g= --===============1589401353==--