Description:
A bug the merge_buffers function may cause wrong results for some queries.
How to repeat:
This bug can be more or less easily demonstrated if you change a parameters of the call
unique= new Unique(refpos_order_cmp, (void *)file,
file->ref_length,
thd->variables.sortbuff_size);
in the QUICK_INDEX_MERGE_SELECT::read_keys_and_merge method.
Make it
unique= new Unique(refpos_order_cmp, (void *)file,
file->ref_length, 64);
Unfortunately currently one can't set the value of the set variable
sort_buffer_size to such a small value.
Yet, we need this setting to come to an execution of the merge_buffers procedure when running the following query on the mysql world database:
mysql> select * from City where Name like 'Pas%' or Population between 100000 and 101000;
The server returns:
ERROR 1032 (HY000): Can't find record in 'City'
To create and populate the database you can use the include files from mysql-test/include/world_schema.inc and mysql-test/include/world.inc from any mysql 5.5/5.4 :
set names utf8;
CREATE DATABASE world;
use world;
source include/world_schema.inc;
Unfortunately currently one can't set the value of the set variable
sort_buffer_size to such a small number.
Yet, we need this setting to come to an execution of the merge_buffers procedure when running the following query on the mysql world database:
mysql> select * from City where Name like 'Pas%' or Population between 100000 and 101000;
The server returns:
ERROR 1032 (HY000): Can't find record in 'City'
To create and populate the database you can use the include files from mysql-test/include/world_schema.inc and mysql-test/include/world.inc of any mysql 5.5/5.4 tree:
set names utf8;
CREATE DATABASE world;
use world;
source include/world_schema.inc;
source include/world.inc;
Then you have to add another index:
CREATE INDEX Name ON City(Name);
After this you have to run:
ALTER TABLE City ENGINE=InnoDB;
Suggested fix:
The following code in merge_buffers looks very suspicious:
buffpek= (BUFFPEK*) queue_top(&queue);
memcpy(param->unique_buff, buffpek->key, rec_length);
if (my_b_write(to_file, (uchar*) buffpek->key, rec_length))
{
error=1; goto err; /* purecov: inspected */
}
buffpek->key+= rec_length;
buffpek->mem_count--;
if (!--max_rows)
{
error= 0; /* purecov: inspected */
goto end; /* purecov: inspected */
}
queue_replaced(&queue); // Top element has been used
If I change it for the code:
buffpek= (BUFFPEK*) queue_top(&queue);
memcpy(param->unique_buff, buffpek->key, rec_length);
if (my_b_write(to_file, (uchar*) buffpek->key, rec_length))
{
error=1; goto err; /* purecov: inspected */
}
if (!--max_rows)
{
error= 0; /* purecov: inspected */
goto end; /* purecov: inspected */
}
buffpek->key+= rec_length;
if (! --buffpek->mem_count)
{
if (!(error= (int) read_to_buffer(from_file,buffpek,
rec_length)))
{
VOID(queue_remove(&queue,0));
reuse_freed_buff(&queue, buffpek, rec_length);
}
else if (error == -1)
goto err; /* purecov: inspected */
}
the reported failure disappears.