Commit 01511453 authored by Ramil Kalimullin's avatar Ramil Kalimullin

Fix for bug #37756: enabling fulltext indexes with

  myisam_repair_threads > 1 causes crash

Problem: parallel repair (myisam_repair_threads > 1) of a myisam
table with two or more fulltext keys that use the same parser may
lead to a server crash. ALTER TABLE ENABLE KEYS is affected as well.

Fix: properly initialize fulltext structures for parallel repair.

Note: 1. there's no deterministic test case.
2. now we call parser->init() for each fulltext key
(not for each fulltext parser used).
parent 75342ddd
...@@ -185,7 +185,7 @@ typedef struct st_mi_keydef /* Key definition with open & info */ ...@@ -185,7 +185,7 @@ typedef struct st_mi_keydef /* Key definition with open & info */
uint16 maxlength; /* max length of (packed) key (auto) */ uint16 maxlength; /* max length of (packed) key (auto) */
uint16 block_size_index; /* block_size (auto) */ uint16 block_size_index; /* block_size (auto) */
uint32 version; /* For concurrent read/write */ uint32 version; /* For concurrent read/write */
uint32 ftparser_nr; /* distinct ftparser number */ uint32 ftkey_nr; /* full-text index number */
HA_KEYSEG *seg,*end; HA_KEYSEG *seg,*end;
struct st_mysql_ftparser *parser; /* Fulltext [pre]parser */ struct st_mysql_ftparser *parser; /* Fulltext [pre]parser */
......
...@@ -323,60 +323,41 @@ int ft_parse(TREE *wtree, uchar *doc, int doclen, ...@@ -323,60 +323,41 @@ int ft_parse(TREE *wtree, uchar *doc, int doclen,
DBUG_RETURN(parser->parse(param)); DBUG_RETURN(parser->parse(param));
} }
#define MAX_PARAM_NR 2 #define MAX_PARAM_NR 2
MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info,
uint keynr, uint paramnr) MYSQL_FTPARSER_PARAM* ftparser_alloc_param(MI_INFO *info)
{ {
uint32 ftparser_nr; if (!info->ftparser_param)
struct st_mysql_ftparser *parser;
if (! info->ftparser_param)
{ {
/* info->ftparser_param can not be zero after the initialization, /*
because it always includes built-in fulltext parser. And built-in . info->ftparser_param can not be zero after the initialization,
parser can be called even if the table has no fulltext indexes and because it always includes built-in fulltext parser. And built-in
no varchar/text fields. */ parser can be called even if the table has no fulltext indexes and
if (! info->s->ftparsers) no varchar/text fields.
{
/* It's ok that modification to shared structure is done w/o mutex ftb_find_relevance... parser (ftb_find_relevance_parse,
locks, because all threads would set the same variables to the ftb_find_relevance_add_word) calls ftb_check_phrase... parser
same values. */ (ftb_check_phrase_internal, ftb_phrase_add_word). Thus MAX_PARAM_NR=2.
uint i, j, keys= info->s->state.header.keys, ftparsers= 1;
for (i= 0; i < keys; i++)
{
MI_KEYDEF *keyinfo= &info->s->keyinfo[i];
if (keyinfo->flag & HA_FULLTEXT)
{
for (j= 0;; j++)
{
if (j == i)
{
keyinfo->ftparser_nr= ftparsers++;
break;
}
if (info->s->keyinfo[j].flag & HA_FULLTEXT &&
keyinfo->parser == info->s->keyinfo[j].parser)
{
keyinfo->ftparser_nr= info->s->keyinfo[j].ftparser_nr;
break;
}
}
}
}
info->s->ftparsers= ftparsers;
}
/*
We have to allocate two MYSQL_FTPARSER_PARAM structures per plugin
because in a boolean search a parser is called recursively
ftb_find_relevance* calls ftb_check_phrase*
(MAX_PARAM_NR=2)
*/ */
info->ftparser_param= (MYSQL_FTPARSER_PARAM *) info->ftparser_param= (MYSQL_FTPARSER_PARAM *)
my_malloc(MAX_PARAM_NR * sizeof(MYSQL_FTPARSER_PARAM) * my_malloc(MAX_PARAM_NR * sizeof(MYSQL_FTPARSER_PARAM) *
info->s->ftparsers, MYF(MY_WME|MY_ZEROFILL)); info->s->ftkeys, MYF(MY_WME | MY_ZEROFILL));
init_alloc_root(&info->ft_memroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0); init_alloc_root(&info->ft_memroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0);
if (! info->ftparser_param)
return 0;
} }
return info->ftparser_param;
}
MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info,
uint keynr, uint paramnr)
{
uint32 ftparser_nr;
struct st_mysql_ftparser *parser;
if (!ftparser_alloc_param(info))
return 0;
if (keynr == NO_SUCH_KEY) if (keynr == NO_SUCH_KEY)
{ {
ftparser_nr= 0; ftparser_nr= 0;
...@@ -384,7 +365,7 @@ MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info, ...@@ -384,7 +365,7 @@ MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info,
} }
else else
{ {
ftparser_nr= info->s->keyinfo[keynr].ftparser_nr; ftparser_nr= info->s->keyinfo[keynr].ftkey_nr;
parser= info->s->keyinfo[keynr].parser; parser= info->s->keyinfo[keynr].parser;
} }
DBUG_ASSERT(paramnr < MAX_PARAM_NR); DBUG_ASSERT(paramnr < MAX_PARAM_NR);
...@@ -416,7 +397,7 @@ void ftparser_call_deinitializer(MI_INFO *info) ...@@ -416,7 +397,7 @@ void ftparser_call_deinitializer(MI_INFO *info)
for (j=0; j < MAX_PARAM_NR; j++) for (j=0; j < MAX_PARAM_NR; j++)
{ {
MYSQL_FTPARSER_PARAM *ftparser_param= MYSQL_FTPARSER_PARAM *ftparser_param=
&info->ftparser_param[keyinfo->ftparser_nr*MAX_PARAM_NR + j]; &info->ftparser_param[keyinfo->ftkey_nr * MAX_PARAM_NR + j];
if (keyinfo->flag & HA_FULLTEXT && ftparser_param->mysql_add_word) if (keyinfo->flag & HA_FULLTEXT && ftparser_param->mysql_add_word)
{ {
if (keyinfo->parser->deinit) if (keyinfo->parser->deinit)
......
...@@ -146,6 +146,7 @@ void ft_boolean_close_search(FT_INFO *); ...@@ -146,6 +146,7 @@ void ft_boolean_close_search(FT_INFO *);
float ft_boolean_get_relevance(FT_INFO *); float ft_boolean_get_relevance(FT_INFO *);
my_off_t ft_boolean_get_docid(FT_INFO *); my_off_t ft_boolean_get_docid(FT_INFO *);
void ft_boolean_reinit_search(FT_INFO *); void ft_boolean_reinit_search(FT_INFO *);
MYSQL_FTPARSER_PARAM* ftparser_alloc_param(MI_INFO *info);
extern MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info, extern MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info,
uint keynr, uint keynr,
uint paramnr); uint paramnr);
......
...@@ -2397,7 +2397,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, ...@@ -2397,7 +2397,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
Note, built-in parser is always nr. 0 - see ftparser_call_initializer() Note, built-in parser is always nr. 0 - see ftparser_call_initializer()
*/ */
if (sort_param.keyinfo->ftparser_nr == 0) if (sort_param.keyinfo->ftkey_nr == 0)
{ {
/* /*
for built-in parser the number of generated index entries for built-in parser the number of generated index entries
...@@ -2895,6 +2895,9 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, ...@@ -2895,6 +2895,9 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
sort_param[0].fix_datafile= (my_bool)(! rep_quick); sort_param[0].fix_datafile= (my_bool)(! rep_quick);
sort_param[0].calc_checksum= test(param->testflag & T_CALC_CHECKSUM); sort_param[0].calc_checksum= test(param->testflag & T_CALC_CHECKSUM);
if (!ftparser_alloc_param(info))
goto err;
sort_info.got_error=0; sort_info.got_error=0;
pthread_mutex_lock(&sort_info.mutex); pthread_mutex_lock(&sort_info.mutex);
......
...@@ -331,6 +331,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) ...@@ -331,6 +331,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
share->blocksize=min(IO_SIZE,myisam_block_size); share->blocksize=min(IO_SIZE,myisam_block_size);
{ {
HA_KEYSEG *pos=share->keyparts; HA_KEYSEG *pos=share->keyparts;
uint32 ftkey_nr= 1;
for (i=0 ; i < keys ; i++) for (i=0 ; i < keys ; i++)
{ {
share->keyinfo[i].share= share; share->keyinfo[i].share= share;
...@@ -412,6 +413,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) ...@@ -412,6 +413,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
share->ft2_keyinfo.end=pos; share->ft2_keyinfo.end=pos;
setup_key_functions(& share->ft2_keyinfo); setup_key_functions(& share->ft2_keyinfo);
} }
share->keyinfo[i].ftkey_nr= ftkey_nr++;
} }
setup_key_functions(share->keyinfo+i); setup_key_functions(share->keyinfo+i);
share->keyinfo[i].end=pos; share->keyinfo[i].end=pos;
...@@ -421,6 +423,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) ...@@ -421,6 +423,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
pos->flag=0; /* For purify */ pos->flag=0; /* For purify */
pos++; pos++;
} }
for (i=0 ; i < uniques ; i++) for (i=0 ; i < uniques ; i++)
{ {
disk_pos=mi_uniquedef_read(disk_pos, &share->uniqueinfo[i]); disk_pos=mi_uniquedef_read(disk_pos, &share->uniqueinfo[i]);
...@@ -449,7 +452,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) ...@@ -449,7 +452,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
pos->flag=0; pos->flag=0;
pos++; pos++;
} }
share->ftparsers= 0; share->ftkeys= ftkey_nr;
} }
disk_pos_assert(disk_pos + share->base.fields *MI_COLUMNDEF_SIZE, end_pos); disk_pos_assert(disk_pos + share->base.fields *MI_COLUMNDEF_SIZE, end_pos);
...@@ -1112,7 +1115,7 @@ uchar *mi_keydef_read(uchar *ptr, MI_KEYDEF *keydef) ...@@ -1112,7 +1115,7 @@ uchar *mi_keydef_read(uchar *ptr, MI_KEYDEF *keydef)
keydef->underflow_block_length=keydef->block_length/3; keydef->underflow_block_length=keydef->block_length/3;
keydef->version = 0; /* Not saved */ keydef->version = 0; /* Not saved */
keydef->parser = &ft_default_parser; keydef->parser = &ft_default_parser;
keydef->ftparser_nr = 0; keydef->ftkey_nr = 0;
return ptr; return ptr;
} }
......
...@@ -193,7 +193,7 @@ typedef struct st_mi_isam_share { /* Shared between opens */ ...@@ -193,7 +193,7 @@ typedef struct st_mi_isam_share { /* Shared between opens */
ulong state_diff_length; ulong state_diff_length;
uint rec_reflength; /* rec_reflength in use now */ uint rec_reflength; /* rec_reflength in use now */
uint unique_name_length; uint unique_name_length;
uint32 ftparsers; /* Number of distinct ftparsers + 1 */ uint32 ftkeys; /* Number of full-text keys + 1 */
File kfile; /* Shared keyfile */ File kfile; /* Shared keyfile */
File data_file; /* Shared data file */ File data_file; /* Shared data file */
int mode; /* mode of file on open */ int mode; /* mode of file on open */
......
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