summaryrefslogtreecommitdiff
path: root/sql/item_strfunc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_strfunc.cc')
-rw-r--r--sql/item_strfunc.cc307
1 files changed, 229 insertions, 78 deletions
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 0bdf396a975..a057594706e 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -80,6 +80,20 @@ String *Item_str_func::check_well_formed_result(String *str)
}
+bool Item_str_func::fix_fields(THD *thd, Item **ref)
+{
+ bool res= Item_func::fix_fields(thd, ref);
+ /*
+ In Item_str_func::check_well_formed_result() we may set null_value
+ flag on the same condition as in test() below.
+ */
+ maybe_null= (maybe_null ||
+ test(thd->variables.sql_mode &
+ (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)));
+ return res;
+}
+
+
my_decimal *Item_str_func::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
@@ -951,24 +965,35 @@ String *Item_func_insert::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res,*res2;
- uint start,length;
+ longlong start, length; /* must be longlong to avoid truncation */
null_value=0;
res=args[0]->val_str(str);
res2=args[3]->val_str(&tmp_value);
- start=(uint) args[1]->val_int()-1;
- length=(uint) args[2]->val_int();
+ start= args[1]->val_int() - 1;
+ length= args[2]->val_int();
+
if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
args[3]->null_value)
goto null; /* purecov: inspected */
- start=res->charpos(start);
- length=res->charpos(length,start);
- if (start > res->length()+1)
- return res; // Wrong param; skip insert
- if (length > res->length()-start)
- length=res->length()-start;
- if (res->length() - length + res2->length() >
- current_thd->variables.max_allowed_packet)
+
+ if ((start < 0) || (start > res->length() + 1))
+ return res; // Wrong param; skip insert
+ if ((length < 0) || (length > res->length() + 1))
+ length= res->length() + 1;
+
+ /* start and length are now sufficiently valid to pass to charpos function */
+ start= res->charpos((int) start);
+ length= res->charpos((int) length, (uint32) start);
+
+ /* Re-testing with corrected params */
+ if (start > res->length() + 1)
+ return res; // Wrong param; skip insert
+ if (length > res->length() - start)
+ length= res->length() - start;
+
+ if ((ulonglong) (res->length() - length + res2->length()) >
+ (ulonglong) current_thd->variables.max_allowed_packet)
{
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
@@ -977,7 +1002,7 @@ String *Item_func_insert::val_str(String *str)
goto null;
}
res=copy_if_not_alloced(str,res,res->length());
- res->replace(start,length,*res2);
+ res->replace((uint32) start,(uint32) length,*res2);
return res;
null:
null_value=1;
@@ -1039,16 +1064,21 @@ String *Item_str_conv::val_str(String *str)
String *Item_func_left::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- String *res =args[0]->val_str(str);
- long length =(long) args[1]->val_int();
+ String *res= args[0]->val_str(str);
+
+ /* must be longlong to avoid truncation */
+ longlong length= args[1]->val_int();
uint char_pos;
if ((null_value=(args[0]->null_value || args[1]->null_value)))
return 0;
- if (length <= 0)
+
+ /* if "unsigned_flag" is set, we have a *huge* positive number. */
+ if ((length <= 0) && (!args[1]->unsigned_flag))
return &my_empty_string;
- if (res->length() <= (uint) length ||
- res->length() <= (char_pos= res->charpos(length)))
+
+ if ((res->length() <= (ulonglong) length) ||
+ (res->length() <= (char_pos= res->charpos((int) length))))
return res;
tmp_value.set(*res, 0, char_pos);
@@ -1080,14 +1110,18 @@ void Item_func_left::fix_length_and_dec()
String *Item_func_right::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- String *res =args[0]->val_str(str);
- long length =(long) args[1]->val_int();
+ String *res= args[0]->val_str(str);
+ /* must be longlong to avoid truncation */
+ longlong length= args[1]->val_int();
if ((null_value=(args[0]->null_value || args[1]->null_value)))
return 0; /* purecov: inspected */
- if (length <= 0)
+
+ /* if "unsigned_flag" is set, we have a *huge* positive number. */
+ if ((length <= 0) && (!args[1]->unsigned_flag))
return &my_empty_string; /* purecov: inspected */
- if (res->length() <= (uint) length)
+
+ if (res->length() <= (ulonglong) length)
return res; /* purecov: inspected */
uint start=res->numchars();
@@ -1110,25 +1144,43 @@ String *Item_func_substr::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
- int32 start = (int32) args[1]->val_int();
- int32 length = arg_count == 3 ? (int32) args[2]->val_int() : INT_MAX32;
- int32 tmp_length;
+ /* must be longlong to avoid truncation */
+ longlong start= args[1]->val_int();
+ /* Assumes that the maximum length of a String is < INT_MAX32. */
+ /* Limit so that code sees out-of-bound value properly. */
+ longlong length= arg_count == 3 ? args[2]->val_int() : INT_MAX32;
+ longlong tmp_length;
if ((null_value=(args[0]->null_value || args[1]->null_value ||
(arg_count == 3 && args[2]->null_value))))
return 0; /* purecov: inspected */
- start= (int32)((start < 0) ? res->numchars() + start : start -1);
- start=res->charpos(start);
- length=res->charpos(length,start);
- if (start < 0 || (uint) start+1 > res->length() || length <= 0)
+
+ /* Negative length, will return empty string. */
+ if ((arg_count == 3) && (length <= 0) && !args[2]->unsigned_flag)
return &my_empty_string;
- tmp_length=(int32) res->length()-start;
- length=min(length,tmp_length);
+ /* 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 ((length <= 0) || (length > INT_MAX32))
+ length= INT_MAX32;
- if (!start && res->length() == (uint) length)
+ /* if "unsigned_flag" is set, we have a *huge* positive number. */
+ /* Assumes that the maximum length of a String is < INT_MAX32. */
+ if ((args[1]->unsigned_flag) || (start < INT_MIN32) || (start > INT_MAX32))
+ return &my_empty_string;
+
+ 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;
+
+ length= res->charpos((int) length, (uint32) start);
+ tmp_length= res->length() - start;
+ length= min(length, tmp_length);
+
+ if (!start && (longlong) res->length() == length)
return res;
- tmp_value.set(*res,(uint) start,(uint) length);
+ tmp_value.set(*res, (uint32) start, (uint32) length);
return &tmp_value;
}
@@ -1621,21 +1673,33 @@ String *Item_func_encrypt::val_str(String *str)
void Item_func_encode::fix_length_and_dec()
{
max_length=args[0]->max_length;
- maybe_null=args[0]->maybe_null;
+ maybe_null=args[0]->maybe_null || args[1]->maybe_null;
collation.set(&my_charset_bin);
}
String *Item_func_encode::val_str(String *str)
{
- DBUG_ASSERT(fixed == 1);
String *res;
+ char pw_buff[80];
+ String tmp_pw_value(pw_buff, sizeof(pw_buff), system_charset_info);
+ String *password;
+ DBUG_ASSERT(fixed == 1);
+
if (!(res=args[0]->val_str(str)))
{
null_value=1; /* purecov: inspected */
return 0; /* purecov: inspected */
}
+
+ if (!(password=args[1]->val_str(& tmp_pw_value)))
+ {
+ null_value=1;
+ return 0;
+ }
+
null_value=0;
res=copy_if_not_alloced(str,res,res->length());
+ SQL_CRYPT sql_crypt(password->ptr());
sql_crypt.init();
sql_crypt.encode((char*) res->ptr(),res->length());
res->set_charset(&my_charset_bin);
@@ -1644,15 +1708,27 @@ String *Item_func_encode::val_str(String *str)
String *Item_func_decode::val_str(String *str)
{
- DBUG_ASSERT(fixed == 1);
String *res;
+ char pw_buff[80];
+ String tmp_pw_value(pw_buff, sizeof(pw_buff), system_charset_info);
+ String *password;
+ DBUG_ASSERT(fixed == 1);
+
if (!(res=args[0]->val_str(str)))
{
null_value=1; /* purecov: inspected */
return 0; /* purecov: inspected */
}
+
+ if (!(password=args[1]->val_str(& tmp_pw_value)))
+ {
+ null_value=1;
+ return 0;
+ }
+
null_value=0;
res=copy_if_not_alloced(str,res,res->length());
+ SQL_CRYPT sql_crypt(password->ptr());
sql_crypt.init();
sql_crypt.decode((char*) res->ptr(),res->length());
return res;
@@ -1827,9 +1903,19 @@ String *Item_func_soundex::val_str(String *str)
** This should be 'internationalized' sometimes.
*/
-Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org)
+const int FORMAT_MAX_DECIMALS= 30;
+
+Item_func_format::Item_func_format(Item *org, Item *dec)
+: Item_str_func(org, dec)
+{
+}
+
+void Item_func_format::fix_length_and_dec()
{
- decimals=(uint) set_zone(dec,0,30);
+ collation.set(default_charset());
+ uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen;
+ max_length= ((char_length + (char_length-args[0]->decimals)/3) *
+ collation.collation->mbmaxlen);
}
@@ -1840,10 +1926,25 @@ Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org)
String *Item_func_format::val_str(String *str)
{
- uint32 length, str_length ,dec;
+ 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;
DBUG_ASSERT(fixed == 1);
- dec= decimals ? decimals+1 : 0;
+
+ dec= args[1]->val_int();
+ if (args[1]->null_value)
+ {
+ null_value=1;
+ return NULL;
+ }
+
+ dec= set_zone(dec, 0, FORMAT_MAX_DECIMALS);
+ dec_length= dec ? dec+1 : 0;
+ null_value=0;
if (args[0]->result_type() == DECIMAL_RESULT ||
args[0]->result_type() == INT_RESULT)
@@ -1852,7 +1953,7 @@ String *Item_func_format::val_str(String *str)
res= args[0]->val_decimal(&dec_val);
if ((null_value=args[0]->null_value))
return 0; /* purecov: inspected */
- my_decimal_round(E_DEC_FATAL_ERROR, res, decimals, false, &rnd_dec);
+ 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())
@@ -1863,9 +1964,9 @@ String *Item_func_format::val_str(String *str)
double nr= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0; /* purecov: inspected */
- nr= my_double_round(nr, decimals, FALSE);
+ nr= my_double_round(nr, dec, FALSE);
/* Here default_charset() is right as this is not an automatic conversion */
- str->set_real(nr,decimals, default_charset());
+ str->set_real(nr, dec, default_charset());
if (isnan(nr))
return str;
str_length=str->length();
@@ -1873,13 +1974,13 @@ String *Item_func_format::val_str(String *str)
str_length--; // Don't count sign
}
/* We need this test to handle 'nan' values */
- if (str_length >= dec+4)
+ if (str_length >= dec_length+4)
{
char *tmp,*pos;
- length= str->length()+(diff=((int)(str_length- dec-1))/3);
+ 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-1;
+ tmp= (char*) str->ptr()+length - dec_length-1;
for (pos= (char*) str->ptr()+length-1; pos != tmp; pos--)
pos[0]= pos[-diff];
while (diff)
@@ -1903,12 +2004,8 @@ void Item_func_format::print(String *str)
{
str->append(STRING_WITH_LEN("format("));
args[0]->print(str);
- str->append(',');
- // my_charset_bin is good enough for numbers
- char buffer[20];
- String st(buffer, sizeof(buffer), &my_charset_bin);
- st.set((ulonglong)decimals, &my_charset_bin);
- str->append(st);
+ str->append(',');
+ args[1]->print(str);
str->append(')');
}
@@ -2146,8 +2243,15 @@ void Item_func_repeat::fix_length_and_dec()
collation.set(args[0]->collation);
if (args[1]->const_item())
{
- ulonglong max_result_length= ((ulonglong) args[0]->max_length *
- args[1]->val_int());
+ /* must be longlong to avoid truncation */
+ longlong count= 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 (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;
@@ -2172,13 +2276,20 @@ String *Item_func_repeat::val_str(String *str)
DBUG_ASSERT(fixed == 1);
uint length,tot_length;
char *to;
- long count= (long) args[1]->val_int();
- String *res =args[0]->val_str(str);
+ /* must be longlong to avoid truncation */
+ longlong tmp_count= args[1]->val_int();
+ long count= (long) tmp_count;
+ String *res= args[0]->val_str(str);
+
+ /* Assumes that the maximum length of a String is < INT_MAX32. */
+ /* Bounds check on count: If this is triggered, we will error. */
+ if ((tmp_count > INT_MAX32) || args[1]->unsigned_flag)
+ count= INT_MAX32;
if (args[0]->null_value || args[1]->null_value)
goto err; // string and/or delim are null
- null_value=0;
- if (count <= 0) // For nicer SQL code
+ null_value= 0;
+ if ((tmp_count <= 0) && !args[1]->unsigned_flag) // For nicer SQL code
return &my_empty_string;
if (count == 1) // To avoid reallocs
return res;
@@ -2217,8 +2328,20 @@ void Item_func_rpad::fix_length_and_dec()
return;
if (args[1]->const_item())
{
- ulonglong length= ((ulonglong) args[1]->val_int() *
- collation.collation->mbmaxlen);
+ 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;
@@ -2240,21 +2363,30 @@ String *Item_func_rpad::val_str(String *str)
uint32 res_byte_length,res_char_length,pad_char_length,pad_byte_length;
char *to;
const char *ptr_pad;
- int32 count= (int32) args[1]->val_int();
- int32 byte_count= count * collation.collation->mbmaxlen;
- String *res =args[0]->val_str(str);
- String *rpad = args[2]->val_str(&rpad_str);
+ /* must be longlong to avoid truncation */
+ longlong count= args[1]->val_int();
+ longlong byte_count;
+ String *res= args[0]->val_str(str);
+ String *rpad= args[2]->val_str(&rpad_str);
+
+ /* 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 ((count > INT_MAX32) || args[1]->unsigned_flag)
+ count= INT_MAX32;
if (!res || args[1]->null_value || !rpad || count < 0)
goto err;
null_value=0;
- if (count <= (int32) (res_char_length=res->numchars()))
+
+ if (count <= (res_char_length= res->numchars()))
{ // String to pad is big enough
- res->length(res->charpos(count)); // Shorten result if longer
+ res->length(res->charpos((int) count)); // Shorten result if longer
return (res);
}
pad_char_length= rpad->numchars();
- if ((ulong) byte_count > current_thd->variables.max_allowed_packet)
+
+ byte_count= count * collation.collation->mbmaxlen;
+ if ((ulonglong) byte_count > current_thd->variables.max_allowed_packet)
{
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
@@ -2265,7 +2397,7 @@ String *Item_func_rpad::val_str(String *str)
if (args[2]->null_value || !pad_char_length)
goto err;
res_byte_length= res->length(); /* Must be done before alloc_buffer */
- if (!(res= alloc_buffer(res,str,&tmp_value,byte_count)))
+ if (!(res= alloc_buffer(res,str,&tmp_value, (ulong) byte_count)))
goto err;
to= (char*) res->ptr()+res_byte_length;
@@ -2279,7 +2411,7 @@ String *Item_func_rpad::val_str(String *str)
}
if (count)
{
- pad_byte_length= rpad->charpos(count);
+ pad_byte_length= rpad->charpos((int) count);
memcpy(to,ptr_pad,(size_t) pad_byte_length);
to+= pad_byte_length;
}
@@ -2300,8 +2432,20 @@ void Item_func_lpad::fix_length_and_dec()
if (args[1]->const_item())
{
- ulonglong length= ((ulonglong) args[1]->val_int() *
- collation.collation->mbmaxlen);
+ 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;
@@ -2321,26 +2465,32 @@ String *Item_func_lpad::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
uint32 res_char_length,pad_char_length;
- ulong count= (long) args[1]->val_int(), byte_count;
+ /* must be longlong to avoid truncation */
+ longlong count= args[1]->val_int();
+ longlong byte_count;
String *res= args[0]->val_str(&tmp_value);
String *pad= args[2]->val_str(&lpad_str);
- if (!res || args[1]->null_value || !pad)
- goto err;
+ /* 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 ((count > INT_MAX32) || args[1]->unsigned_flag)
+ count= INT_MAX32;
+ if (!res || args[1]->null_value || !pad || count < 0)
+ goto err;
null_value=0;
res_char_length= res->numchars();
if (count <= res_char_length)
{
- res->length(res->charpos(count));
+ res->length(res->charpos((int) count));
return res;
}
pad_char_length= pad->numchars();
byte_count= count * collation.collation->mbmaxlen;
- if (byte_count > current_thd->variables.max_allowed_packet)
+ if ((ulonglong) byte_count > current_thd->variables.max_allowed_packet)
{
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
@@ -2349,7 +2499,8 @@ String *Item_func_lpad::val_str(String *str)
goto err;
}
- if (args[2]->null_value || !pad_char_length || str->alloc(byte_count))
+ if (args[2]->null_value || !pad_char_length ||
+ str->alloc((uint32) byte_count))
goto err;
str->length(0);
@@ -2361,7 +2512,7 @@ String *Item_func_lpad::val_str(String *str)
count-= pad_char_length;
}
if (count > 0)
- str->append(pad->ptr(), pad->charpos(count), collation.collation);
+ str->append(pad->ptr(), pad->charpos((int) count), collation.collation);
str->append(*res);
null_value= 0;
@@ -2970,6 +3121,7 @@ String *Item_func_compress::val_str(String *str)
null_value= 1;
return 0;
}
+ null_value= 0;
if (res->is_empty()) return res;
/*
@@ -3180,4 +3332,3 @@ String *Item_func_uuid::val_str(String *str)
strmov(s+18, clock_seq_and_node_str);
return str;
}
-