Merge spetrunia@bk-internal.mysql.com:/home/bk/mysql-4.1
into mysql.com:/home/psergey/mysql-4.1-nulls-stats-r2 sql/mysqld.cc: Auto merged
This commit is contained in:
commit
3c02a0534d
@ -319,6 +319,8 @@ enum ha_base_keytype {
|
|||||||
#define SEARCH_NULL_ARE_EQUAL 32768 /* NULL in keys are equal */
|
#define SEARCH_NULL_ARE_EQUAL 32768 /* NULL in keys are equal */
|
||||||
#define SEARCH_NULL_ARE_NOT_EQUAL 65536 /* NULL in keys are not equal */
|
#define SEARCH_NULL_ARE_NOT_EQUAL 65536 /* NULL in keys are not equal */
|
||||||
|
|
||||||
|
#define SEARCH_RETURN_B_POS (65536*2) /* see ha_key_cmp for description */
|
||||||
|
|
||||||
/* bits in opt_flag */
|
/* bits in opt_flag */
|
||||||
#define QUICK_USED 1
|
#define QUICK_USED 1
|
||||||
#define READ_CACHE_USED 2
|
#define READ_CACHE_USED 2
|
||||||
|
@ -63,4 +63,6 @@ extern int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
|||||||
register uchar *b, uint key_length, uint nextflag,
|
register uchar *b, uint key_length, uint nextflag,
|
||||||
uint *diff_pos);
|
uint *diff_pos);
|
||||||
|
|
||||||
|
extern HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, uchar *a);
|
||||||
|
|
||||||
#endif /* _my_handler_h */
|
#endif /* _my_handler_h */
|
||||||
|
@ -322,7 +322,9 @@ typedef enum
|
|||||||
/* Treat NULLs as inequal when collecting statistics (default for 4.1/5.0) */
|
/* Treat NULLs as inequal when collecting statistics (default for 4.1/5.0) */
|
||||||
MI_STATS_METHOD_NULLS_NOT_EQUAL,
|
MI_STATS_METHOD_NULLS_NOT_EQUAL,
|
||||||
/* Treat NULLs as equal when collecting statistics (like 4.0 did) */
|
/* Treat NULLs as equal when collecting statistics (like 4.0 did) */
|
||||||
MI_STATS_METHOD_NULLS_EQUAL
|
MI_STATS_METHOD_NULLS_EQUAL,
|
||||||
|
/* Ignore NULLs - count only tuples without NULLs in the index components */
|
||||||
|
MI_STATS_METHOD_IGNORE_NULLS
|
||||||
} enum_mi_stats_method;
|
} enum_mi_stats_method;
|
||||||
|
|
||||||
typedef struct st_mi_check_param
|
typedef struct st_mi_check_param
|
||||||
@ -349,7 +351,14 @@ typedef struct st_mi_check_param
|
|||||||
int tmpfile_createflag;
|
int tmpfile_createflag;
|
||||||
myf myf_rw;
|
myf myf_rw;
|
||||||
IO_CACHE read_cache;
|
IO_CACHE read_cache;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The next two are used to collect statistics, see update_key_parts for
|
||||||
|
description.
|
||||||
|
*/
|
||||||
ulonglong unique_count[MI_MAX_KEY_SEG+1];
|
ulonglong unique_count[MI_MAX_KEY_SEG+1];
|
||||||
|
ulonglong notnull_count[MI_MAX_KEY_SEG+1];
|
||||||
|
|
||||||
ha_checksum key_crc[MI_MAX_POSSIBLE_KEY];
|
ha_checksum key_crc[MI_MAX_POSSIBLE_KEY];
|
||||||
ulong rec_per_key_part[MI_MAX_KEY_SEG*MI_MAX_POSSIBLE_KEY];
|
ulong rec_per_key_part[MI_MAX_KEY_SEG*MI_MAX_POSSIBLE_KEY];
|
||||||
void *thd;
|
void *thd;
|
||||||
@ -409,7 +418,8 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
|
|||||||
my_bool repair);
|
my_bool repair);
|
||||||
int update_state_info(MI_CHECK *param, MI_INFO *info,uint update);
|
int update_state_info(MI_CHECK *param, MI_INFO *info,uint update);
|
||||||
void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
|
void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
|
||||||
ulonglong *unique, ulonglong records);
|
ulonglong *unique, ulonglong *notnull,
|
||||||
|
ulonglong records);
|
||||||
int filecopy(MI_CHECK *param, File to,File from,my_off_t start,
|
int filecopy(MI_CHECK *param, File to,File from,my_off_t start,
|
||||||
my_off_t length, const char *type);
|
my_off_t length, const char *type);
|
||||||
int movepoint(MI_INFO *info,byte *record,my_off_t oldpos,
|
int movepoint(MI_INFO *info,byte *record,my_off_t oldpos,
|
||||||
|
@ -391,7 +391,10 @@ int chk_key(MI_CHECK *param, register MI_INFO *info)
|
|||||||
found_keys++;
|
found_keys++;
|
||||||
|
|
||||||
param->record_checksum=init_checksum;
|
param->record_checksum=init_checksum;
|
||||||
|
|
||||||
bzero((char*) ¶m->unique_count,sizeof(param->unique_count));
|
bzero((char*) ¶m->unique_count,sizeof(param->unique_count));
|
||||||
|
bzero((char*) ¶m->notnull_count,sizeof(param->notnull_count));
|
||||||
|
|
||||||
if ((!(param->testflag & T_SILENT)))
|
if ((!(param->testflag & T_SILENT)))
|
||||||
printf ("- check data record references index: %d\n",key+1);
|
printf ("- check data record references index: %d\n",key+1);
|
||||||
if (keyinfo->flag & HA_FULLTEXT)
|
if (keyinfo->flag & HA_FULLTEXT)
|
||||||
@ -496,7 +499,9 @@ int chk_key(MI_CHECK *param, register MI_INFO *info)
|
|||||||
|
|
||||||
if (param->testflag & T_STATISTICS)
|
if (param->testflag & T_STATISTICS)
|
||||||
update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
|
update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
|
||||||
(ulonglong) info->state->records);
|
param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
|
||||||
|
param->notnull_count: NULL,
|
||||||
|
(ulonglong)info->state->records);
|
||||||
}
|
}
|
||||||
if (param->testflag & T_INFO)
|
if (param->testflag & T_INFO)
|
||||||
{
|
{
|
||||||
@ -552,6 +557,96 @@ err:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
"Ignore NULLs" statistics collection method: process first index tuple.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
mi_collect_stats_nonulls_first()
|
||||||
|
keyseg IN Array of key part descriptions
|
||||||
|
notnull INOUT Array, notnull[i] = (number of {keypart1...keypart_i}
|
||||||
|
tuples that don't contain NULLs)
|
||||||
|
key IN Key values tuple
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Process the first index tuple - find out which prefix tuples don't
|
||||||
|
contain NULLs, and update the array of notnull counters accordingly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static
|
||||||
|
void mi_collect_stats_nonulls_first(HA_KEYSEG *keyseg, ulonglong *notnull,
|
||||||
|
uchar *key)
|
||||||
|
{
|
||||||
|
uint first_null, kp;
|
||||||
|
first_null= ha_find_null(keyseg, key) - keyseg;
|
||||||
|
/*
|
||||||
|
All prefix tuples that don't include keypart_{first_null} are not-null
|
||||||
|
tuples (and all others aren't), increment counters for them.
|
||||||
|
*/
|
||||||
|
for (kp= 0; kp < first_null; kp++)
|
||||||
|
notnull[kp]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
"Ignore NULLs" statistics collection method: process next index tuple.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
mi_collect_stats_nonulls_next()
|
||||||
|
keyseg IN Array of key part descriptions
|
||||||
|
notnull INOUT Array, notnull[i] = (number of {keypart1...keypart_i}
|
||||||
|
tuples that don't contain NULLs)
|
||||||
|
prev_key IN Previous key values tuple
|
||||||
|
last_key IN Next key values tuple
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Process the next index tuple:
|
||||||
|
1. Find out which prefix tuples of last_key don't contain NULLs, and
|
||||||
|
update the array of notnull counters accordingly.
|
||||||
|
2. Find the first keypart number where the prev_key and last_key tuples
|
||||||
|
are different(A), or last_key has NULL value(B), and return it, so the
|
||||||
|
caller can count number of unique tuples for each key prefix. We don't
|
||||||
|
need (B) to be counted, and that is compensated back in
|
||||||
|
update_key_parts().
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
1 + number of first keypart where values differ or last_key tuple has NULL
|
||||||
|
*/
|
||||||
|
|
||||||
|
static
|
||||||
|
int mi_collect_stats_nonulls_next(HA_KEYSEG *keyseg, ulonglong *notnull,
|
||||||
|
uchar *prev_key, uchar *last_key)
|
||||||
|
{
|
||||||
|
uint diffs[2];
|
||||||
|
uint first_null_seg, kp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Find the first keypart where values are different or either of them is
|
||||||
|
NULL. We get results in diffs array:
|
||||||
|
diffs[0]= 1 + number of first different keypart
|
||||||
|
diffs[1]=offset: (last_key + diffs[1]) points to first value in
|
||||||
|
last_key that is NULL or different from corresponding
|
||||||
|
value in prev_key.
|
||||||
|
*/
|
||||||
|
ha_key_cmp(keyseg, prev_key, last_key, USE_WHOLE_KEY,
|
||||||
|
SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL | SEARCH_RETURN_B_POS,
|
||||||
|
diffs);
|
||||||
|
HA_KEYSEG *seg= keyseg + diffs[0] - 1;
|
||||||
|
|
||||||
|
/* Find first NULL in last_key */
|
||||||
|
first_null_seg= ha_find_null(seg, last_key + diffs[1]) - keyseg;
|
||||||
|
for (kp= 0; kp < first_null_seg; kp++)
|
||||||
|
notnull[kp]++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return 1+ number of first key part where values differ. Don't care if
|
||||||
|
these were NULLs and not .... We compensate for that in
|
||||||
|
update_key_parts.
|
||||||
|
*/
|
||||||
|
return diffs[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Check if index is ok */
|
/* Check if index is ok */
|
||||||
|
|
||||||
static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
|
static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
|
||||||
@ -641,8 +736,20 @@ static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
|
|||||||
ha_key_cmp(keyinfo->seg,info->lastkey,key,USE_WHOLE_KEY,
|
ha_key_cmp(keyinfo->seg,info->lastkey,key,USE_WHOLE_KEY,
|
||||||
SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL,
|
SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL,
|
||||||
&diff_pos);
|
&diff_pos);
|
||||||
|
else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
|
||||||
|
{
|
||||||
|
diff_pos= mi_collect_stats_nonulls_next(keyinfo->seg,
|
||||||
|
param->notnull_count,
|
||||||
|
info->lastkey, key);
|
||||||
|
}
|
||||||
param->unique_count[diff_pos-1]++;
|
param->unique_count[diff_pos-1]++;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
|
||||||
|
mi_collect_stats_nonulls_first(keyinfo->seg, param->notnull_count,
|
||||||
|
key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(*key_checksum)+= mi_byte_checksum((byte*) key,
|
(*key_checksum)+= mi_byte_checksum((byte*) key,
|
||||||
key_length- info->s->rec_reflength);
|
key_length- info->s->rec_reflength);
|
||||||
@ -2088,7 +2195,8 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
|
|||||||
|
|
||||||
if (param->testflag & T_STATISTICS)
|
if (param->testflag & T_STATISTICS)
|
||||||
update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique,
|
update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique,
|
||||||
(ulonglong) info->state->records);
|
param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
|
||||||
|
sort_param.notnull: NULL,(ulonglong) info->state->records);
|
||||||
share->state.key_map|=(ulonglong) 1 << sort_param.key;
|
share->state.key_map|=(ulonglong) 1 << sort_param.key;
|
||||||
|
|
||||||
if (sort_param.fix_datafile)
|
if (sort_param.fix_datafile)
|
||||||
@ -3255,11 +3363,21 @@ static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a)
|
|||||||
ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
|
ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
|
||||||
(uchar*) a, USE_WHOLE_KEY,
|
(uchar*) a, USE_WHOLE_KEY,
|
||||||
SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, &diff_pos);
|
SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, &diff_pos);
|
||||||
|
else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
|
||||||
|
{
|
||||||
|
diff_pos= mi_collect_stats_nonulls_next(sort_param->seg,
|
||||||
|
sort_param->notnull,
|
||||||
|
sort_info->key_block->lastkey,
|
||||||
|
(uchar*)a);
|
||||||
|
}
|
||||||
sort_param->unique[diff_pos-1]++;
|
sort_param->unique[diff_pos-1]++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cmp= -1;
|
cmp= -1;
|
||||||
|
if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
|
||||||
|
mi_collect_stats_nonulls_first(sort_param->seg, sort_param->notnull,
|
||||||
|
(uchar*)a);
|
||||||
}
|
}
|
||||||
if ((sort_param->keyinfo->flag & HA_NOSAME) && cmp == 0)
|
if ((sort_param->keyinfo->flag & HA_NOSAME) && cmp == 0)
|
||||||
{
|
{
|
||||||
@ -3978,24 +4096,34 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Update statistics for each part of an index
|
Update statistics for each part of an index
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
update_key_parts()
|
update_key_parts()
|
||||||
keyinfo Index information (only key->keysegs used)
|
keyinfo IN Index information (only key->keysegs used)
|
||||||
rec_per_key_part OUT Store statistics here
|
rec_per_key_part OUT Store statistics here
|
||||||
unique IN Array of #distinct values collected over index
|
unique IN Array of (#distinct tuples)
|
||||||
run.
|
notnull_tuples IN Array of (#tuples), or NULL
|
||||||
records Number of records in the table
|
records Number of records in the table
|
||||||
|
|
||||||
NOTES
|
DESCRIPTION
|
||||||
|
This function is called produce index statistics values from unique and
|
||||||
|
notnull_tuples arrays after these arrays were produced with sequential
|
||||||
|
index scan (the scan is done in two places: chk_index() and
|
||||||
|
sort_key_write()).
|
||||||
|
|
||||||
|
This function handles all 3 index statistics collection methods.
|
||||||
|
|
||||||
Unique is an array:
|
Unique is an array:
|
||||||
unique[0]= (#different values of {keypart1}) - 1
|
unique[0]= (#different values of {keypart1}) - 1
|
||||||
unique[1]= (#different values of {keypart2,keypart1} tuple) - unique[0] - 1
|
unique[1]= (#different values of {keypart1,keypart2} tuple)-unique[0]-1
|
||||||
...
|
...
|
||||||
The 'unique' array is collected in one sequential scan through the entire
|
|
||||||
index. This is done in two places: in chk_index() and in sort_key_write().
|
For MI_STATS_METHOD_IGNORE_NULLS method, notnull_tuples is an array too:
|
||||||
Statistics collection may consider NULLs as either equal or unequal (see
|
notnull_tuples[0]= (#of {keypart1} tuples such that keypart1 is not NULL)
|
||||||
SEARCH_NULL_ARE_NOT_EQUAL, MI_STATS_METHOD_*).
|
notnull_tuples[1]= (#of {keypart1,keypart2} tuples such that all
|
||||||
|
keypart{i} are not NULL)
|
||||||
|
...
|
||||||
|
For all other statistics collection methods notnull_tuples==NULL.
|
||||||
|
|
||||||
Output is an array:
|
Output is an array:
|
||||||
rec_per_key_part[k] =
|
rec_per_key_part[k] =
|
||||||
@ -4007,25 +4135,53 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
|
|||||||
index tuples}
|
index tuples}
|
||||||
|
|
||||||
= #tuples-in-the-index / #distinct-tuples-in-the-index.
|
= #tuples-in-the-index / #distinct-tuples-in-the-index.
|
||||||
|
|
||||||
|
The #tuples-in-the-index and #distinct-tuples-in-the-index have different
|
||||||
|
meaning depending on which statistics collection method is used:
|
||||||
|
|
||||||
|
MI_STATS_METHOD_* how are nulls compared? which tuples are counted?
|
||||||
|
NULLS_EQUAL NULL == NULL all tuples in table
|
||||||
|
NULLS_NOT_EQUAL NULL != NULL all tuples in table
|
||||||
|
IGNORE_NULLS n/a tuples that don't have NULLs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
|
void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
|
||||||
ulonglong *unique, ulonglong records)
|
ulonglong *unique, ulonglong *notnull,
|
||||||
|
ulonglong records)
|
||||||
{
|
{
|
||||||
ulonglong count=0,tmp;
|
ulonglong count=0,tmp, unique_tuples;
|
||||||
|
ulonglong tuples= records;
|
||||||
uint parts;
|
uint parts;
|
||||||
for (parts=0 ; parts < keyinfo->keysegs ; parts++)
|
for (parts=0 ; parts < keyinfo->keysegs ; parts++)
|
||||||
{
|
{
|
||||||
count+=unique[parts];
|
count+=unique[parts];
|
||||||
if (count == 0)
|
unique_tuples= count + 1;
|
||||||
tmp=records;
|
if (notnull)
|
||||||
|
{
|
||||||
|
tuples= notnull[parts];
|
||||||
|
/*
|
||||||
|
#(unique_tuples not counting tuples with NULLs) =
|
||||||
|
#(unique_tuples counting tuples with NULLs as different) -
|
||||||
|
#(tuples with NULLs)
|
||||||
|
*/
|
||||||
|
unique_tuples -= (records - notnull[parts]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unique_tuples == 0)
|
||||||
|
tmp= 1;
|
||||||
|
else if (count == 0)
|
||||||
|
tmp= tuples; /* 1 unique tuple */
|
||||||
else
|
else
|
||||||
tmp= (records + (count+1)/2) / (count+1);
|
tmp= (tuples + unique_tuples/2) / unique_tuples;
|
||||||
/* for some weird keys (e.g. FULLTEXT) tmp can be <1 here.
|
|
||||||
let's ensure it is not */
|
/*
|
||||||
|
for some weird keys (e.g. FULLTEXT) tmp can be <1 here.
|
||||||
|
let's ensure it is not
|
||||||
|
*/
|
||||||
set_if_bigger(tmp,1);
|
set_if_bigger(tmp,1);
|
||||||
if (tmp >= (ulonglong) ~(ulong) 0)
|
if (tmp >= (ulonglong) ~(ulong) 0)
|
||||||
tmp=(ulonglong) ~(ulong) 0;
|
tmp=(ulonglong) ~(ulong) 0;
|
||||||
|
|
||||||
*rec_per_key_part=(ulong) tmp;
|
*rec_per_key_part=(ulong) tmp;
|
||||||
rec_per_key_part++;
|
rec_per_key_part++;
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,8 @@ static struct my_option my_long_options[] =
|
|||||||
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||||
{"stats_method", OPT_STATS_METHOD,
|
{"stats_method", OPT_STATS_METHOD,
|
||||||
"Specifies how index statistics collection code should threat NULLs. "
|
"Specifies how index statistics collection code should threat NULLs. "
|
||||||
"Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), and \"nulls_equal\" (emulate 4.0 behavior).",
|
"Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), "
|
||||||
|
"\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".",
|
||||||
(gptr*) &myisam_stats_method_str, (gptr*) &myisam_stats_method_str, 0,
|
(gptr*) &myisam_stats_method_str, (gptr*) &myisam_stats_method_str, 0,
|
||||||
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||||
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
||||||
@ -451,6 +452,10 @@ static void usage(void)
|
|||||||
-a, --analyze Analyze distribution of keys. Will make some joins in\n\
|
-a, --analyze Analyze distribution of keys. Will make some joins in\n\
|
||||||
MySQL faster. You can check the calculated distribution\n\
|
MySQL faster. You can check the calculated distribution\n\
|
||||||
by using '--description --verbose table_name'.\n\
|
by using '--description --verbose table_name'.\n\
|
||||||
|
--stats_method=name Specifies how index statistics collection code should\n\
|
||||||
|
threat NULLs. Possible values of name are \"nulls_unequal\"\n\
|
||||||
|
(default for 4.1/5.0), \"nulls_equal\" (emulate 4.0), and \n\
|
||||||
|
\"nulls_ignored\".\n\
|
||||||
-d, --description Prints some information about table.\n\
|
-d, --description Prints some information about table.\n\
|
||||||
-A, --set-auto-increment[=value]\n\
|
-A, --set-auto-increment[=value]\n\
|
||||||
Force auto_increment to start at this or higher value\n\
|
Force auto_increment to start at this or higher value\n\
|
||||||
@ -472,7 +477,7 @@ static void usage(void)
|
|||||||
#include <help_end.h>
|
#include <help_end.h>
|
||||||
|
|
||||||
const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal",
|
const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal",
|
||||||
NullS};
|
"nulls_ignored", NullS};
|
||||||
TYPELIB myisam_stats_method_typelib= {
|
TYPELIB myisam_stats_method_typelib= {
|
||||||
array_elements(myisam_stats_method_names) - 1, "",
|
array_elements(myisam_stats_method_names) - 1, "",
|
||||||
myisam_stats_method_names, NULL};
|
myisam_stats_method_names, NULL};
|
||||||
@ -699,14 +704,25 @@ get_one_option(int optid,
|
|||||||
case OPT_STATS_METHOD:
|
case OPT_STATS_METHOD:
|
||||||
{
|
{
|
||||||
int method;
|
int method;
|
||||||
|
enum_mi_stats_method method_conv;
|
||||||
myisam_stats_method_str= argument;
|
myisam_stats_method_str= argument;
|
||||||
if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0)
|
if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Invalid value of stats_method: %s.\n", argument);
|
fprintf(stderr, "Invalid value of stats_method: %s.\n", argument);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
check_param.stats_method= test(method-1)? MI_STATS_METHOD_NULLS_EQUAL :
|
switch (method-1) {
|
||||||
MI_STATS_METHOD_NULLS_NOT_EQUAL;
|
case 0:
|
||||||
|
method_conv= MI_STATS_METHOD_NULLS_EQUAL;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
method_conv= MI_STATS_METHOD_IGNORE_NULLS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
check_param.stats_method= method_conv;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#ifdef DEBUG /* Only useful if debugging */
|
#ifdef DEBUG /* Only useful if debugging */
|
||||||
|
@ -297,7 +297,14 @@ typedef struct st_mi_sort_param
|
|||||||
pthread_t thr;
|
pthread_t thr;
|
||||||
IO_CACHE read_cache, tempfile, tempfile_for_exceptions;
|
IO_CACHE read_cache, tempfile, tempfile_for_exceptions;
|
||||||
DYNAMIC_ARRAY buffpek;
|
DYNAMIC_ARRAY buffpek;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The next two are used to collect statistics, see update_key_parts for
|
||||||
|
description.
|
||||||
|
*/
|
||||||
ulonglong unique[MI_MAX_KEY_SEG+1];
|
ulonglong unique[MI_MAX_KEY_SEG+1];
|
||||||
|
ulonglong notnull[MI_MAX_KEY_SEG+1];
|
||||||
|
|
||||||
my_off_t pos,max_pos,filepos,start_recpos;
|
my_off_t pos,max_pos,filepos,start_recpos;
|
||||||
uint key, key_length,real_key_length,sortbuff_size;
|
uint key, key_length,real_key_length,sortbuff_size;
|
||||||
uint maxbuffers, keys, find_length, sort_keys_length;
|
uint maxbuffers, keys, find_length, sort_keys_length;
|
||||||
|
@ -481,8 +481,12 @@ int thr_write_keys(MI_SORT_PARAM *sort_param)
|
|||||||
{
|
{
|
||||||
share->state.key_map|=(ulonglong) 1 << sinfo->key;
|
share->state.key_map|=(ulonglong) 1 << sinfo->key;
|
||||||
if (param->testflag & T_STATISTICS)
|
if (param->testflag & T_STATISTICS)
|
||||||
update_key_parts(sinfo->keyinfo, rec_per_key_part,
|
update_key_parts(sinfo->keyinfo, rec_per_key_part, sinfo->unique,
|
||||||
sinfo->unique, (ulonglong) info->state->records);
|
param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
|
||||||
|
sinfo->notnull: NULL,
|
||||||
|
(ulonglong) info->state->records);
|
||||||
|
|
||||||
|
|
||||||
if (!sinfo->buffpek.elements)
|
if (!sinfo->buffpek.elements)
|
||||||
{
|
{
|
||||||
if (param->testflag & T_VERBOSE)
|
if (param->testflag & T_VERBOSE)
|
||||||
|
@ -670,3 +670,35 @@ show index from t1;
|
|||||||
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
|
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
|
||||||
t1 1 a 1 a A 10 NULL NULL YES BTREE
|
t1 1 a 1 a A 10 NULL NULL YES BTREE
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
set myisam_stats_method=nulls_ignored;
|
||||||
|
show variables like 'myisam_stats_method';
|
||||||
|
Variable_name Value
|
||||||
|
myisam_stats_method nulls_ignored
|
||||||
|
create table t1 (
|
||||||
|
a char(3), b char(4), c char(5), d char(6),
|
||||||
|
key(a,b,c,d)
|
||||||
|
);
|
||||||
|
insert into t1 values ('bcd','def1', NULL, 'zz');
|
||||||
|
insert into t1 values ('bcd','def2', NULL, 'zz');
|
||||||
|
insert into t1 values ('bce','def1', 'yuu', NULL);
|
||||||
|
insert into t1 values ('bce','def2', NULL, 'quux');
|
||||||
|
analyze table t1;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.t1 analyze status OK
|
||||||
|
show index from t1;
|
||||||
|
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
|
||||||
|
t1 1 a 1 a A 2 NULL NULL YES BTREE
|
||||||
|
t1 1 a 2 b A 4 NULL NULL YES BTREE
|
||||||
|
t1 1 a 3 c A 4 NULL NULL YES BTREE
|
||||||
|
t1 1 a 4 d A 4 NULL NULL YES BTREE
|
||||||
|
delete from t1;
|
||||||
|
analyze table t1;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.t1 analyze status OK
|
||||||
|
show index from t1;
|
||||||
|
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
|
||||||
|
t1 1 a 1 a A 0 NULL NULL YES BTREE
|
||||||
|
t1 1 a 2 b A 0 NULL NULL YES BTREE
|
||||||
|
t1 1 a 3 c A 0 NULL NULL YES BTREE
|
||||||
|
t1 1 a 4 d A 0 NULL NULL YES BTREE
|
||||||
|
set myisam_stats_method=DEFAULT;
|
||||||
|
@ -637,4 +637,23 @@ show index from t1;
|
|||||||
|
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
# WL#2609, CSC#XXXX: MyISAM
|
||||||
|
set myisam_stats_method=nulls_ignored;
|
||||||
|
show variables like 'myisam_stats_method';
|
||||||
|
|
||||||
|
create table t1 (
|
||||||
|
a char(3), b char(4), c char(5), d char(6),
|
||||||
|
key(a,b,c,d)
|
||||||
|
);
|
||||||
|
insert into t1 values ('bcd','def1', NULL, 'zz');
|
||||||
|
insert into t1 values ('bcd','def2', NULL, 'zz');
|
||||||
|
insert into t1 values ('bce','def1', 'yuu', NULL);
|
||||||
|
insert into t1 values ('bce','def2', NULL, 'quux');
|
||||||
|
analyze table t1;
|
||||||
|
show index from t1;
|
||||||
|
delete from t1;
|
||||||
|
analyze table t1;
|
||||||
|
show index from t1;
|
||||||
|
|
||||||
|
set myisam_stats_method=DEFAULT;
|
||||||
# End of 4.1 tests
|
# End of 4.1 tests
|
||||||
|
@ -75,7 +75,7 @@ static int compare_bin(uchar *a, uint a_length, uchar *b, uint b_length,
|
|||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
ha_key_cmp()
|
ha_key_cmp()
|
||||||
keyseg Key segments of key to compare
|
keyseg Array of key segments of key to compare
|
||||||
a First key to compare, in format from _mi_pack_key()
|
a First key to compare, in format from _mi_pack_key()
|
||||||
This is normally key specified by user
|
This is normally key specified by user
|
||||||
b Second key to compare. This is always from a row
|
b Second key to compare. This is always from a row
|
||||||
@ -84,10 +84,32 @@ static int compare_bin(uchar *a, uint a_length, uchar *b, uint b_length,
|
|||||||
next_flag How keys should be compared
|
next_flag How keys should be compared
|
||||||
If bit SEARCH_FIND is not set the keys includes the row
|
If bit SEARCH_FIND is not set the keys includes the row
|
||||||
position and this should also be compared
|
position and this should also be compared
|
||||||
|
diff_pos OUT Number of first keypart where values differ, counting
|
||||||
|
from one.
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
If SEARCH_RETURN_B_POS flag is set, diff_pos must point to array of 2
|
||||||
|
values, first value has the meaning as described in parameter
|
||||||
|
description above, the second value is:
|
||||||
|
|
||||||
|
diff_pos[1] OUT (b + diff_pos[1]) points to first value in tuple b
|
||||||
|
that is different from corresponding value in tuple a.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
Example1: if the function is called for tuples
|
||||||
|
('aaa','bbb') and ('eee','fff'), then
|
||||||
|
diff_pos[0] = 1 (as 'aaa' != 'eee')
|
||||||
|
diff_pos[1] = 0 (offset from beggining of tuple b to 'eee' keypart).
|
||||||
|
|
||||||
|
Example2: if the index function is called for tuples
|
||||||
|
('aaa','bbb') and ('aaa','fff'),
|
||||||
|
diff_pos[0] = 2 (as 'aaa' != 'eee')
|
||||||
|
diff_pos[1] = 3 (offset from beggining of tuple b to 'fff' keypart,
|
||||||
|
here we assume that first key part is CHAR(3) NOT NULL)
|
||||||
|
|
||||||
NOTES
|
NOTES
|
||||||
Number-keys can't be splited
|
Number-keys can't be splited
|
||||||
|
|
||||||
RETURN VALUES
|
RETURN VALUES
|
||||||
<0 If a < b
|
<0 If a < b
|
||||||
0 If a == b
|
0 If a == b
|
||||||
@ -107,6 +129,7 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
|||||||
float f_1,f_2;
|
float f_1,f_2;
|
||||||
double d_1,d_2;
|
double d_1,d_2;
|
||||||
uint next_key_length;
|
uint next_key_length;
|
||||||
|
uchar *orig_b= b;
|
||||||
|
|
||||||
*diff_pos=0;
|
*diff_pos=0;
|
||||||
for ( ; (int) key_length >0 ; key_length=next_key_length, keyseg++)
|
for ( ; (int) key_length >0 ; key_length=next_key_length, keyseg++)
|
||||||
@ -115,6 +138,9 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
|||||||
uint piks=! (keyseg->flag & HA_NO_SORT);
|
uint piks=! (keyseg->flag & HA_NO_SORT);
|
||||||
(*diff_pos)++;
|
(*diff_pos)++;
|
||||||
|
|
||||||
|
if (nextflag & SEARCH_RETURN_B_POS)
|
||||||
|
diff_pos[1]= (uint)(b - orig_b);
|
||||||
|
|
||||||
/* Handle NULL part */
|
/* Handle NULL part */
|
||||||
if (keyseg->null_bit)
|
if (keyseg->null_bit)
|
||||||
{
|
{
|
||||||
@ -448,3 +474,84 @@ end:
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} /* ha_key_cmp */
|
} /* ha_key_cmp */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Find the first NULL value in index-suffix values tuple
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
ha_find_null()
|
||||||
|
keyseg Array of keyparts for key suffix
|
||||||
|
a Key suffix value tuple
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Find the first NULL value in index-suffix values tuple.
|
||||||
|
TODO Consider optimizing this fuction or its use so we don't search for
|
||||||
|
NULL values in completely NOT NULL index suffixes.
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
First key part that has NULL as value in values tuple, or the last key part
|
||||||
|
(with keyseg->type==HA_TYPE_END) if values tuple doesn't contain NULLs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, uchar *a)
|
||||||
|
{
|
||||||
|
for (; (enum ha_base_keytype) keyseg->type != HA_KEYTYPE_END; keyseg++)
|
||||||
|
{
|
||||||
|
uchar *end;
|
||||||
|
if (keyseg->null_bit)
|
||||||
|
{
|
||||||
|
if (!*a++)
|
||||||
|
return keyseg;
|
||||||
|
}
|
||||||
|
end= a+ keyseg->length;
|
||||||
|
|
||||||
|
switch ((enum ha_base_keytype) keyseg->type) {
|
||||||
|
case HA_KEYTYPE_TEXT:
|
||||||
|
case HA_KEYTYPE_BINARY:
|
||||||
|
if (keyseg->flag & HA_SPACE_PACK)
|
||||||
|
{
|
||||||
|
int a_length;
|
||||||
|
get_key_length(a_length, a);
|
||||||
|
a += a_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
a= end;
|
||||||
|
break;
|
||||||
|
case HA_KEYTYPE_VARTEXT:
|
||||||
|
case HA_KEYTYPE_VARBINARY:
|
||||||
|
{
|
||||||
|
int a_length;
|
||||||
|
get_key_length(a_length, a);
|
||||||
|
a+= a_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HA_KEYTYPE_NUM:
|
||||||
|
if (keyseg->flag & HA_SPACE_PACK)
|
||||||
|
{
|
||||||
|
int alength= *a++;
|
||||||
|
end= a+alength;
|
||||||
|
}
|
||||||
|
a= end;
|
||||||
|
break;
|
||||||
|
case HA_KEYTYPE_INT8:
|
||||||
|
case HA_KEYTYPE_SHORT_INT:
|
||||||
|
case HA_KEYTYPE_USHORT_INT:
|
||||||
|
case HA_KEYTYPE_LONG_INT:
|
||||||
|
case HA_KEYTYPE_ULONG_INT:
|
||||||
|
case HA_KEYTYPE_INT24:
|
||||||
|
case HA_KEYTYPE_UINT24:
|
||||||
|
#ifdef HAVE_LONG_LONG
|
||||||
|
case HA_KEYTYPE_LONGLONG:
|
||||||
|
case HA_KEYTYPE_ULONGLONG:
|
||||||
|
#endif
|
||||||
|
case HA_KEYTYPE_FLOAT:
|
||||||
|
case HA_KEYTYPE_DOUBLE:
|
||||||
|
a= end;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keyseg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"",
|
|||||||
myisam_recover_names, NULL};
|
myisam_recover_names, NULL};
|
||||||
|
|
||||||
const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal",
|
const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal",
|
||||||
NullS};
|
"nulls_ignored", NullS};
|
||||||
TYPELIB myisam_stats_method_typelib= {
|
TYPELIB myisam_stats_method_typelib= {
|
||||||
array_elements(myisam_stats_method_names) - 1, "",
|
array_elements(myisam_stats_method_names) - 1, "",
|
||||||
myisam_stats_method_names, NULL};
|
myisam_stats_method_names, NULL};
|
||||||
|
@ -5281,7 +5281,8 @@ The minimum value for this variable is 4096.",
|
|||||||
GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0},
|
GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0},
|
||||||
{"myisam_stats_method", OPT_MYISAM_STATS_METHOD,
|
{"myisam_stats_method", OPT_MYISAM_STATS_METHOD,
|
||||||
"Specifies how MyISAM index statistics collection code should threat NULLs. "
|
"Specifies how MyISAM index statistics collection code should threat NULLs. "
|
||||||
"Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), and \"nulls_equal\" (emulate 4.0 behavior).",
|
"Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), "
|
||||||
|
"\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".",
|
||||||
(gptr*) &myisam_stats_method_str, (gptr*) &myisam_stats_method_str, 0,
|
(gptr*) &myisam_stats_method_str, (gptr*) &myisam_stats_method_str, 0,
|
||||||
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||||
{"net_buffer_length", OPT_NET_BUFFER_LENGTH,
|
{"net_buffer_length", OPT_NET_BUFFER_LENGTH,
|
||||||
@ -6475,16 +6476,26 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
|||||||
}
|
}
|
||||||
case OPT_MYISAM_STATS_METHOD:
|
case OPT_MYISAM_STATS_METHOD:
|
||||||
{
|
{
|
||||||
myisam_stats_method_str= argument;
|
|
||||||
int method;
|
int method;
|
||||||
|
ulong method_conv;
|
||||||
|
myisam_stats_method_str= argument;
|
||||||
if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0)
|
if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Invalid value of myisam_stats_method: %s.\n", argument);
|
fprintf(stderr, "Invalid value of myisam_stats_method: %s.\n", argument);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
global_system_variables.myisam_stats_method=
|
switch (method-1) {
|
||||||
test(method-1)? MI_STATS_METHOD_NULLS_EQUAL :
|
case 0:
|
||||||
MI_STATS_METHOD_NULLS_NOT_EQUAL;
|
method_conv= MI_STATS_METHOD_NULLS_EQUAL;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
method_conv= MI_STATS_METHOD_IGNORE_NULLS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
global_system_variables.myisam_stats_method= method_conv;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OPT_SQL_MODE:
|
case OPT_SQL_MODE:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user