Bug #48617 A bug in the merge_buffers code
Submitted: 8 Nov 2009 6:47 Modified: 19 Nov 2009 10:57
Reporter: Igor Babaev Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: General Severity:S2 (Serious)
Version:5.1 OS:Any
Assigned to: CPU Architecture:Any
Tags: Contribution

[8 Nov 2009 6:47] Igor Babaev
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.
[8 Nov 2009 6:55] Igor Babaev
Correction for the synopsis:
"A bug in the merge_buffers code"
[19 Nov 2009 10:57] Sveta Smirnova
Thank you for the report.

Verified as described, although I had to make

  unique= new Unique(refpos_order_cmp, (void *)file,
                     file->ref_length, 48);

not 64
[24 Dec 2009 20:01] MySQL Verification Team
perhaps related to bug #49897