Commit c4a908cb authored by Alexander Barkov's avatar Alexander Barkov

MDEV-13790 UNHEX() of a somewhat complicated CONCAT() returns NULL

parent dae4fb0a
......@@ -262,3 +262,9 @@ c2
abcdefghi-abcdefghi
DROP TABLE t1;
SET optimizer_switch=@save_optimizer_switch;
#
# MDEV-13790 UNHEX() of a somewhat complicated CONCAT() returns NULL
#
SELECT UNHEX(CONCAT('414C2', HEX(8 + ROUND(RAND()*7)), SUBSTR(SHA(UUID()),6,33),HEX(2+ROUND(RAND()*8)))) IS NULL AS c1;
c1
0
......@@ -236,3 +236,9 @@ SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT TRIM(t) t2 FROM t1) sub;
DROP TABLE t1;
SET optimizer_switch=@save_optimizer_switch;
--echo #
--echo # MDEV-13790 UNHEX() of a somewhat complicated CONCAT() returns NULL
--echo #
SELECT UNHEX(CONCAT('414C2', HEX(8 + ROUND(RAND()*7)), SUBSTR(SHA(UUID()),6,33),HEX(2+ROUND(RAND()*8)))) IS NULL AS c1;
......@@ -630,138 +630,84 @@ String *Item_func_decode_histogram::val_str(String *str)
///////////////////////////////////////////////////////////////////////////////
/*
Realloc the result buffer.
NOTE: We should be prudent in the initial allocation unit -- the
size of the arguments is a function of data distribution, which
can be any. Instead of overcommitting at the first row, we grow
the allocated amount by the factor of 2. This ensures that no
more than 25% of memory will be overcommitted on average.
@param IN/OUT str - the result string
@param IN length - new total space required in "str"
@retval false - on success
@retval true - on error
*/
bool Item_func_concat::realloc_result(String *str, uint length) const
{
if (str->alloced_length() >= length)
return false; // Alloced space is big enough, nothing to do.
if (str->alloced_length() == 0)
return str->alloc(length);
/*
Item_func_concat::val_str() makes sure the result length does not grow
higher than max_allowed_packet. So "length" is limited to 1G here.
We can't say anything about the current value of str->alloced_length(),
as str was initially set by args[0]->val_str(str).
So multiplication by 2 can overflow, if args[0] for some reasons
did not limit the result to max_alloced_packet. But it's not harmful,
"str" will be realloced exactly to "length" bytes in case of overflow.
*/
uint new_length= MY_MAX(str->alloced_length() * 2, length);
return str->realloc(new_length);
}
/**
Concatenate args with the following premises:
If only one arg (which is ok), return value of arg;
Don't reallocate val_str() if not absolute necessary.
*/
String *Item_func_concat::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res,*res2,*use_as_buff;
uint i;
bool is_const= 0;
THD *thd= current_thd;
String *res;
null_value=0;
if (!(res=args[0]->val_str(str)))
if (!(res= args[0]->val_str(str)))
goto null;
use_as_buff= &tmp_value;
/* Item_subselect in --ps-protocol mode will state it as a non-const */
is_const= args[0]->const_item() || !args[0]->used_tables();
for (i=1 ; i < arg_count ; i++)
{
if (res->length() == 0)
{
if (!(res=args[i]->val_str(str)))
goto null;
/*
CONCAT accumulates its result in the result of its the first
non-empty argument. Because of this we need is_const to be
evaluated only for it.
*/
is_const= args[i]->const_item() || !args[i]->used_tables();
}
else
{
if (!(res2=args[i]->val_str(use_as_buff)))
goto null;
if (res2->length() == 0)
continue;
if (res->length()+res2->length() >
current_thd->variables.max_allowed_packet)
{
push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
current_thd->variables.max_allowed_packet);
goto null;
}
if (!is_const && res->alloced_length() >= res->length()+res2->length())
{ // Use old buffer
res->append(*res2);
}
else if (str->alloced_length() >= res->length()+res2->length())
{
if (str->ptr() == res2->ptr())
str->replace(0,0,*res);
else
{
str->copy(*res);
str->append(*res2);
}
res= str;
use_as_buff= &tmp_value;
}
else if (res == &tmp_value)
{
if (res->append(*res2)) // Must be a blob
goto null;
}
else if (res2 == &tmp_value)
{ // This can happend only 1 time
if (tmp_value.replace(0,0,*res))
goto null;
res= &tmp_value;
use_as_buff=str; // Put next arg here
}
else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length())
{
/*
This happens really seldom:
In this case res2 is sub string of tmp_value. We will
now work in place in tmp_value to set it to res | res2
*/
/* Chop the last characters in tmp_value that isn't in res2 */
tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) +
res2->length());
/* Place res2 at start of tmp_value, remove chars before res2 */
if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()),
*res))
goto null;
res= &tmp_value;
use_as_buff=str; // Put next arg here
}
else
{ // Two big const strings
/*
NOTE: We should be prudent in the initial allocation unit -- the
size of the arguments is a function of data distribution, which
can be any. Instead of overcommitting at the first row, we grow
the allocated amount by the factor of 2. This ensures that no
more than 25% of memory will be overcommitted on average.
*/
uint concat_len= res->length() + res2->length();
if (tmp_value.alloced_length() < concat_len)
{
if (tmp_value.alloced_length() == 0)
{
if (tmp_value.alloc(concat_len))
goto null;
}
else
{
uint new_len = MY_MAX(tmp_value.alloced_length() * 2, concat_len);
if (tmp_value.realloc(new_len))
goto null;
}
}
if (res != str)
str->copy(res->ptr(), res->length(), res->charset());
if (tmp_value.copy(*res) || tmp_value.append(*res2))
goto null;
res= &tmp_value;
use_as_buff=str;
}
is_const= 0;
for (uint i= 1 ; i < arg_count ; i++)
{
uint concat_len;
if (!(res= args[i]->val_str(&tmp_value)))
goto null;
if (res->length() == 0)
continue;
if ((concat_len= str->length() + res->length()) >
thd->variables.max_allowed_packet)
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
thd->variables.max_allowed_packet);
goto null;
}
DBUG_ASSERT(!res->uses_buffer_owned_by(str));
DBUG_ASSERT(!str->uses_buffer_owned_by(res));
if (realloc_result(str, concat_len) || str->append(*res))
goto null;
}
res->set_charset(collation.collation);
return res;
str->set_charset(collation.collation);
return str;
null:
null_value=1;
......
......@@ -156,6 +156,7 @@ class Item_func_aes_decrypt :public Item_str_func
class Item_func_concat :public Item_str_func
{
String tmp_value;
bool realloc_result(String *str, uint length) const;
public:
Item_func_concat(List<Item> &list) :Item_str_func(list) {}
Item_func_concat(Item *a,Item *b) :Item_str_func(a,b) {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment