Commit 62513bb1 authored by Sergey Petrunia's avatar Sergey Petrunia

BUG#35478: sort_union() returns bad data when sort_buffer_size is hit

- In QUICK_INDEX_MERGE_SELECT::read_keys_and_merge: when we got table->sort from Unique,
  tell init_read_record() not to use rr_from_cache() because a) rowids are already sorted
  and b) it might be that the the data is used by filesort(), which will need record rowids
  (which rr_from_cache() cannot provide).
- Fully de-initialize the table->sort read in QUICK_INDEX_MERGE_SELECT::get_next(). This fixes BUG#35477.
(bk trigger: file as fix for BUG#35478).

sql/filesort.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - make find_all_keys() use quick->get_next() instead of init_read_record(r)/r.read_record() calls
  - added dbug printout
sql/mysql_priv.h:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - Added parameter to init_read_record
sql/opt_range.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - In QUICK_INDEX_MERGE_SELECT::read_keys_and_merge: when we got table->sort from Unique,
    tell init_read_record() not to use rr_from_cache() because a) rowids are already sorted
    and b) it might be that the the data is used by filesort(), which will need record rowids
    (which rr_from_cache() cannot provide).
  - Fully de-initialize the table->sort read in QUICK_INDEX_MERGE_SELECT::get_next().
sql/records.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - Added disable_rr_cache parameter to init_read_record
  - Added comment
sql/sql_acl.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - Added parameter to init_read_record
sql/sql_delete.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - Added parameter to init_read_record
sql/sql_help.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - Added parameter to init_read_record
sql/sql_select.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - Added parameter to init_read_record
sql/sql_table.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - Added parameter to init_read_record
sql/sql_udf.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - Added parameter to init_read_record
sql/sql_update.cc:
  BUG#35478: sort_union() returns bad data when sort_buffer_size is hit
  - Added parameter to init_read_record
parent d5077086
...@@ -402,6 +402,56 @@ static byte *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count, ...@@ -402,6 +402,56 @@ static byte *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count,
DBUG_RETURN(tmp); DBUG_RETURN(tmp);
} }
#ifndef DBUG_OFF
/*
Print a text, SQL-like record representation into dbug trace.
Note: this function is a work in progress: at the moment
- column read bitmap is ignored (can print garbage for unused columns)
- there is no quoting
*/
static void dbug_print_record(TABLE *table, bool print_rowid)
{
char buff[1024];
Field **pfield;
String tmp(buff,sizeof(buff),&my_charset_bin);
DBUG_LOCK_FILE;
fprintf(DBUG_FILE, "record (");
for (pfield= table->field; *pfield ; pfield++)
fprintf(DBUG_FILE, "%s%s", (*pfield)->field_name, (pfield[1])? ", ":"");
fprintf(DBUG_FILE, ") = ");
fprintf(DBUG_FILE, "(");
for (pfield= table->field; *pfield ; pfield++)
{
Field *field= *pfield;
if (field->is_null())
fwrite("NULL", sizeof(char), 4, DBUG_FILE);
if (field->type() == MYSQL_TYPE_BIT)
(void) field->val_int_as_str(&tmp, 1);
else
field->val_str(&tmp);
fwrite(tmp.ptr(),sizeof(char),tmp.length(),DBUG_FILE);
if (pfield[1])
fwrite(", ", sizeof(char), 2, DBUG_FILE);
}
fprintf(DBUG_FILE, ")");
if (print_rowid)
{
fprintf(DBUG_FILE, " rowid ");
for (uint i=0; i < table->file->ref_length; i++)
{
fprintf(DBUG_FILE, "%x", (uchar)table->file->ref[i]);
}
}
fprintf(DBUG_FILE, "\n");
DBUG_UNLOCK_FILE;
}
#endif
/* /*
Search after sort_keys and write them into tempfile. Search after sort_keys and write them into tempfile.
...@@ -475,25 +525,23 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, ...@@ -475,25 +525,23 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
current_thd->variables.read_buff_size); current_thd->variables.read_buff_size);
} }
READ_RECORD read_record_info;
if (quick_select) if (quick_select)
{ {
if (select->quick->reset()) if (select->quick->reset())
DBUG_RETURN(HA_POS_ERROR); DBUG_RETURN(HA_POS_ERROR);
init_read_record(&read_record_info, current_thd, select->quick->head,
select, 1, 1);
} }
for (;;) for (;;)
{ {
if (quick_select) if (quick_select)
{ {
if ((error= read_record_info.read_record(&read_record_info))) if ((error= select->quick->get_next()))
{ {
error= HA_ERR_END_OF_FILE; error= HA_ERR_END_OF_FILE;
break; break;
} }
file->position(sort_form->record[0]); file->position(sort_form->record[0]);
DBUG_EXECUTE_IF("debug_filesort", dbug_print_record(sort_form, TRUE););
} }
else /* Not quick-select */ else /* Not quick-select */
{ {
...@@ -550,15 +598,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, ...@@ -550,15 +598,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
if (thd->net.report_error) if (thd->net.report_error)
break; break;
} }
if (quick_select) if (!quick_select)
{
/*
index_merge quick select uses table->sort when retrieving rows, so free
resoures it has allocated.
*/
end_read_record(&read_record_info);
}
else
{ {
(void) file->extra(HA_EXTRA_NO_CACHE); /* End cacheing of records */ (void) file->extra(HA_EXTRA_NO_CACHE); /* End cacheing of records */
if (!next_pos) if (!next_pos)
......
...@@ -1557,8 +1557,8 @@ ulonglong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, ...@@ -1557,8 +1557,8 @@ ulonglong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
int test_if_number(char *str,int *res,bool allow_wildcards); int test_if_number(char *str,int *res,bool allow_wildcards);
void change_byte(byte *,uint,char,char); void change_byte(byte *,uint,char,char);
void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form, void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
SQL_SELECT *select, SQL_SELECT *select, int use_record_cache,
int use_record_cache, bool print_errors); bool print_errors, bool disable_rr_cache);
void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
bool print_error, uint idx); bool print_error, uint idx);
void end_read_record(READ_RECORD *info); void end_read_record(READ_RECORD *info);
......
...@@ -6494,7 +6494,7 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() ...@@ -6494,7 +6494,7 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
QUICK_RANGE_SELECT* cur_quick; QUICK_RANGE_SELECT* cur_quick;
int result; int result;
Unique *unique; Unique *unique;
DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::prepare_unique"); DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::read_keys_and_merge");
/* We're going to just read rowids. */ /* We're going to just read rowids. */
if (head->file->extra(HA_EXTRA_KEYREAD)) if (head->file->extra(HA_EXTRA_KEYREAD))
...@@ -6565,13 +6565,17 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() ...@@ -6565,13 +6565,17 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
} }
/* ok, all row ids are in Unique */ /*
Ok all rowids are in the Unique now. The next call will initialize
head->sort structure so it can be used to iterate through the rowids
sequence.
*/
result= unique->get(head); result= unique->get(head);
delete unique; delete unique;
doing_pk_scan= FALSE; doing_pk_scan= FALSE;
/* start table scan */
init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1, 1); /* Start the rnd_pos() scan. */
/* index_merge currently doesn't support "using index" at all */ init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1 , 1, TRUE);
head->file->extra(HA_EXTRA_NO_KEYREAD); head->file->extra(HA_EXTRA_NO_KEYREAD);
DBUG_RETURN(result); DBUG_RETURN(result);
...@@ -6601,6 +6605,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next() ...@@ -6601,6 +6605,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next()
{ {
result= HA_ERR_END_OF_FILE; result= HA_ERR_END_OF_FILE;
end_read_record(&read_record); end_read_record(&read_record);
free_io_cache(head);
/* All rows from Unique have been retrieved, do a clustered PK scan */ /* All rows from Unique have been retrieved, do a clustered PK scan */
if (pk_quick_select) if (pk_quick_select)
{ {
......
...@@ -72,11 +72,47 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, ...@@ -72,11 +72,47 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
} }
/* init struct for read with info->read_record */ /*
init struct for read with info->read_record
SYNOPSIS
init_read_record()
info OUT read structure
thd Thread handle
table Table the data [originally] comes from.
select SQL_SELECT structure. We may select->quick or
select->file as data source
use_record_cache Call file->extra_opt(HA_EXTRA_CACHE,...)
if we're going to do sequential read and some
additional conditions are satisfied.
print_error Copy this to info->print_error
disable_rr_cache Don't use rr_from_cache (used by sort-union
index-merge which produces rowid sequences that
are already ordered)
DESCRIPTION
This function sets up reading data via one of the methods:
rr_unpack_from_tempfile Unpack full records from sequential file
rr_unpack_from_buffer ... or from buffer
rr_from_tempfile Read rowids from tempfile and get full records
with handler->rnd_pos() calls.
rr_from_pointers ... or get rowids from buffer
rr_from_cache Read a bunch of rowids from file, sort them,
get records in rowid order, return, repeat.
rr_quick Get data from QUICK_*_SELECT
rr_sequential Sequentially scan the table using
handler->rnd_next() calls
*/
void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
SQL_SELECT *select, SQL_SELECT *select,
int use_record_cache, bool print_error) int use_record_cache, bool print_error,
bool disable_rr_cache)
{ {
IO_CACHE *tempfile; IO_CACHE *tempfile;
DBUG_ENTER("init_read_record"); DBUG_ENTER("init_read_record");
...@@ -121,7 +157,8 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, ...@@ -121,7 +157,8 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
it doesn't make sense to use cache - we don't read from the table it doesn't make sense to use cache - we don't read from the table
and table->sort.io_cache is read sequentially and table->sort.io_cache is read sequentially
*/ */
if (!table->sort.addon_field && if (!disable_rr_cache &&
!table->sort.addon_field &&
! (specialflag & SPECIAL_SAFE_MODE) && ! (specialflag & SPECIAL_SAFE_MODE) &&
thd->variables.read_rnd_buff_size && thd->variables.read_rnd_buff_size &&
!(table->file->table_flags() & HA_FAST_KEY_READ) && !(table->file->table_flags() & HA_FAST_KEY_READ) &&
......
...@@ -205,7 +205,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) ...@@ -205,7 +205,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
acl_cache->clear(1); // Clear locked hostname cache acl_cache->clear(1); // Clear locked hostname cache
init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0); init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0); init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0,
FALSE);
VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50)); VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
while (!(read_record_info.read_record(&read_record_info))) while (!(read_record_info.read_record(&read_record_info)))
{ {
...@@ -253,7 +254,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) ...@@ -253,7 +254,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
end_read_record(&read_record_info); end_read_record(&read_record_info);
freeze_size(&acl_hosts); freeze_size(&acl_hosts);
init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0); init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0,FALSE);
VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100)); VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
password_length= table->field[2]->field_length / password_length= table->field[2]->field_length /
table->field[2]->charset()->mbmaxlen; table->field[2]->charset()->mbmaxlen;
...@@ -426,7 +427,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) ...@@ -426,7 +427,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
end_read_record(&read_record_info); end_read_record(&read_record_info);
freeze_size(&acl_users); freeze_size(&acl_users);
init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0); init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0,FALSE);
VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100)); VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
while (!(read_record_info.read_record(&read_record_info))) while (!(read_record_info.read_record(&read_record_info)))
{ {
......
...@@ -214,7 +214,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -214,7 +214,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
if (usable_index==MAX_KEY) if (usable_index==MAX_KEY)
init_read_record(&info,thd,table,select,1,1); init_read_record(&info, thd, table, select, 1, 1, FALSE);
else else
init_read_record_idx(&info, thd, table, 1, usable_index); init_read_record_idx(&info, thd, table, 1, usable_index);
...@@ -772,7 +772,7 @@ int multi_delete::do_deletes() ...@@ -772,7 +772,7 @@ int multi_delete::do_deletes()
} }
READ_RECORD info; READ_RECORD info;
init_read_record(&info,thd,table,NULL,0,1); init_read_record(&info, thd, table, NULL, 0, 1, FALSE);
/* /*
Ignore any rows not found in reference tables as they may already have Ignore any rows not found in reference tables as they may already have
been deleted by foreign key handling been deleted by foreign key handling
......
...@@ -181,7 +181,7 @@ int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields, ...@@ -181,7 +181,7 @@ int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields,
int count= 0; int count= 0;
READ_RECORD read_record_info; READ_RECORD read_record_info;
init_read_record(&read_record_info, thd, topics, select,1,0); init_read_record(&read_record_info, thd, topics, select, 1, 0, FALSE);
while (!read_record_info.read_record(&read_record_info)) while (!read_record_info.read_record(&read_record_info))
{ {
if (!select->cond->val_int()) // Doesn't match like if (!select->cond->val_int()) // Doesn't match like
...@@ -221,7 +221,7 @@ int search_keyword(THD *thd, TABLE *keywords, struct st_find_field *find_fields, ...@@ -221,7 +221,7 @@ int search_keyword(THD *thd, TABLE *keywords, struct st_find_field *find_fields,
int count= 0; int count= 0;
READ_RECORD read_record_info; READ_RECORD read_record_info;
init_read_record(&read_record_info, thd, keywords, select,1,0); init_read_record(&read_record_info, thd, keywords, select, 1, 0, FALSE);
while (!read_record_info.read_record(&read_record_info) && count<2) while (!read_record_info.read_record(&read_record_info) && count<2)
{ {
if (!select->cond->val_int()) // Dosn't match like if (!select->cond->val_int()) // Dosn't match like
...@@ -346,7 +346,7 @@ int search_categories(THD *thd, TABLE *categories, ...@@ -346,7 +346,7 @@ int search_categories(THD *thd, TABLE *categories,
DBUG_ENTER("search_categories"); DBUG_ENTER("search_categories");
init_read_record(&read_record_info, thd, categories, select,1,0); init_read_record(&read_record_info, thd, categories, select,1,0,FALSE);
while (!read_record_info.read_record(&read_record_info)) while (!read_record_info.read_record(&read_record_info))
{ {
if (select && !select->cond->val_int()) if (select && !select->cond->val_int())
...@@ -380,7 +380,7 @@ void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname, ...@@ -380,7 +380,7 @@ void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname,
DBUG_ENTER("get_all_items_for_category"); DBUG_ENTER("get_all_items_for_category");
READ_RECORD read_record_info; READ_RECORD read_record_info;
init_read_record(&read_record_info, thd, items, select,1,0); init_read_record(&read_record_info, thd, items, select,1,0,FALSE);
while (!read_record_info.read_record(&read_record_info)) while (!read_record_info.read_record(&read_record_info))
{ {
if (!select->cond->val_int()) if (!select->cond->val_int())
......
...@@ -11309,7 +11309,7 @@ join_init_read_record(JOIN_TAB *tab) ...@@ -11309,7 +11309,7 @@ join_init_read_record(JOIN_TAB *tab)
if (tab->select && tab->select->quick && tab->select->quick->reset()) if (tab->select && tab->select->quick && tab->select->quick->reset())
return 1; return 1;
init_read_record(&tab->read_record, tab->join->thd, tab->table, init_read_record(&tab->read_record, tab->join->thd, tab->table,
tab->select,1,1); tab->select,1,1, FALSE);
return (*tab->read_record.read_record)(&tab->read_record); return (*tab->read_record.read_record)(&tab->read_record);
} }
......
...@@ -4177,7 +4177,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, ...@@ -4177,7 +4177,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
current query id current query id
*/ */
from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1); init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE);
if (ignore) if (ignore)
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY); to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
thd->row_count= 0; thd->row_count= 0;
......
...@@ -176,7 +176,7 @@ void udf_init() ...@@ -176,7 +176,7 @@ void udf_init()
} }
table= tables.table; table= tables.table;
init_read_record(&read_record_info, new_thd, table, NULL,1,0); init_read_record(&read_record_info, new_thd, table, NULL,1,0,FALSE);
while (!(error = read_record_info.read_record(&read_record_info))) while (!(error = read_record_info.read_record(&read_record_info)))
{ {
DBUG_PRINT("info",("init udf record")); DBUG_PRINT("info",("init udf record"));
......
...@@ -358,7 +358,7 @@ int mysql_update(THD *thd, ...@@ -358,7 +358,7 @@ int mysql_update(THD *thd,
Full index scan must be started with init_read_record_idx Full index scan must be started with init_read_record_idx
*/ */
if (used_index == MAX_KEY || (select && select->quick)) if (used_index == MAX_KEY || (select && select->quick))
init_read_record(&info,thd,table,select,0,1); init_read_record(&info, thd, table, select, 0, 1, FALSE);
else else
init_read_record_idx(&info, thd, table, 1, used_index); init_read_record_idx(&info, thd, table, 1, used_index);
...@@ -422,7 +422,7 @@ int mysql_update(THD *thd, ...@@ -422,7 +422,7 @@ int mysql_update(THD *thd,
if (select && select->quick && select->quick->reset()) if (select && select->quick && select->quick->reset())
goto err; goto err;
init_read_record(&info,thd,table,select,0,1); init_read_record(&info, thd, table, select, 0, 1, FALSE);
updated= found= 0; updated= found= 0;
thd->count_cuted_fields= CHECK_FIELD_WARN; /* calc cuted fields */ thd->count_cuted_fields= CHECK_FIELD_WARN; /* calc cuted fields */
......
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