mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
de5029a458
In order to be able to check if the set of the grouping fields in a GROUP BY has changed (and thus to start a new group) the optimizer caches the current values of these fields in a set of Cached_item derived objects. The Cached_item_str, used for caching varchar and TEXT columns, is limited in length by the max_sort_length variable. A String buffer to store the value with an alloced length of either the max length of the string or the value of max_sort_length (whichever is smaller) in Cached_item_str's constructor. Then, at compare time the value of the string to compare to was truncated to the alloced length of the string buffer inside Cached_item_str. This is all fine and valid, but only if you're not assigning values near or equal to the alloced length of this buffer. Because when assigning values like this the alloced length is rounded up and as a result the next set of data will not match the group buffer, thus leading to wrong results because of the changed alloced_length. Fixed by preserving the original maximum length in the Cached_item_str's constructor and using this instead of the alloced_length to limit the string to compare to. Test case added.
166 lines
3.8 KiB
C++
166 lines
3.8 KiB
C++
/* Copyright (C) 2000-2006 MySQL AB
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
|
|
/**
|
|
@file
|
|
|
|
@brief
|
|
Buffers to save and compare item values
|
|
*/
|
|
|
|
#include "mysql_priv.h"
|
|
|
|
/**
|
|
Create right type of Cached_item for an item.
|
|
*/
|
|
|
|
Cached_item *new_Cached_item(THD *thd, Item *item)
|
|
{
|
|
if (item->real_item()->type() == Item::FIELD_ITEM &&
|
|
!(((Item_field *) (item->real_item()))->field->flags & BLOB_FLAG))
|
|
return new Cached_item_field((Item_field *) (item->real_item()));
|
|
switch (item->result_type()) {
|
|
case STRING_RESULT:
|
|
return new Cached_item_str(thd, (Item_field *) item);
|
|
case INT_RESULT:
|
|
return new Cached_item_int((Item_field *) item);
|
|
case REAL_RESULT:
|
|
return new Cached_item_real(item);
|
|
case DECIMAL_RESULT:
|
|
return new Cached_item_decimal(item);
|
|
case ROW_RESULT:
|
|
default:
|
|
DBUG_ASSERT(0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Cached_item::~Cached_item() {}
|
|
|
|
/**
|
|
Compare with old value and replace value with new value.
|
|
|
|
@return
|
|
Return true if values have changed
|
|
*/
|
|
|
|
Cached_item_str::Cached_item_str(THD *thd, Item *arg)
|
|
:item(arg),
|
|
value_max_length(min(arg->max_length, thd->variables.max_sort_length)),
|
|
value(value_max_length)
|
|
{}
|
|
|
|
bool Cached_item_str::cmp(void)
|
|
{
|
|
String *res;
|
|
bool tmp;
|
|
|
|
if ((res=item->val_str(&tmp_value)))
|
|
res->length(min(res->length(), value_max_length));
|
|
if (null_value != item->null_value)
|
|
{
|
|
if ((null_value= item->null_value))
|
|
return TRUE; // New value was null
|
|
tmp=TRUE;
|
|
}
|
|
else if (null_value)
|
|
return 0; // new and old value was null
|
|
else
|
|
tmp= sortcmp(&value,res,item->collation.collation) != 0;
|
|
if (tmp)
|
|
value.copy(*res); // Remember for next cmp
|
|
return tmp;
|
|
}
|
|
|
|
Cached_item_str::~Cached_item_str()
|
|
{
|
|
item=0; // Safety
|
|
}
|
|
|
|
bool Cached_item_real::cmp(void)
|
|
{
|
|
double nr= item->val_real();
|
|
if (null_value != item->null_value || nr != value)
|
|
{
|
|
null_value= item->null_value;
|
|
value=nr;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool Cached_item_int::cmp(void)
|
|
{
|
|
longlong nr=item->val_int();
|
|
if (null_value != item->null_value || nr != value)
|
|
{
|
|
null_value= item->null_value;
|
|
value=nr;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
bool Cached_item_field::cmp(void)
|
|
{
|
|
bool tmp= field->cmp(buff) != 0; // This is not a blob!
|
|
if (tmp)
|
|
field->get_image(buff,length,field->charset());
|
|
if (null_value != field->is_null())
|
|
{
|
|
null_value= !null_value;
|
|
tmp=TRUE;
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
|
|
Cached_item_decimal::Cached_item_decimal(Item *it)
|
|
:item(it)
|
|
{
|
|
my_decimal_set_zero(&value);
|
|
}
|
|
|
|
|
|
bool Cached_item_decimal::cmp()
|
|
{
|
|
my_decimal tmp;
|
|
my_decimal *ptmp= item->val_decimal(&tmp);
|
|
if (null_value != item->null_value ||
|
|
(!item->null_value && my_decimal_cmp(&value, ptmp)))
|
|
{
|
|
null_value= item->null_value;
|
|
/* Save only not null values */
|
|
if (!null_value)
|
|
{
|
|
my_decimal2decimal(ptmp, &value);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
** Instansiate templates
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
|
|
template class List<Cached_item>;
|
|
template class List_iterator<Cached_item>;
|
|
#endif
|