mirror of
https://github.com/MariaDB/server.git
synced 2025-07-21 21:22:27 +03:00
MDEV-18741: Optimizer trace: multi-part key ranges are printed incorrectly
Changed the function append_range_all_keyparts to use sel_arg_range_seq_init / sel_arg_range_seq_next to produce ranges. Also adjusted to print format for the ranges, now the ranges are printed as: (keypart1_min, keypart2_min,..) OP (keypart1_name,keypart2_name, ..) OP (keypart1_max,keypart2_max, ..) Also added more tests for range and index merge access for optimizer trace
This commit is contained in:
365
sql/opt_range.cc
365
sql/opt_range.cc
@ -328,8 +328,6 @@ public:
|
||||
uint *imerge_cost_buff; /* buffer for index_merge cost estimates */
|
||||
uint imerge_cost_buff_size; /* size of the buffer */
|
||||
|
||||
/* TRUE if last checked tree->key can be used for ROR-scan */
|
||||
bool is_ror_scan;
|
||||
/* Number of ranges in the last checked tree->key */
|
||||
uint n_ranges;
|
||||
uint8 first_null_comp; /* first null component if any, 0 - otherwise */
|
||||
@ -351,7 +349,7 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts);
|
||||
static ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
|
||||
SEL_ARG *tree, bool update_tbl_stats,
|
||||
uint *mrr_flags, uint *bufsize,
|
||||
Cost_estimate *cost);
|
||||
Cost_estimate *cost, bool *is_ror_scan);
|
||||
|
||||
QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index,
|
||||
SEL_ARG *key_tree, uint mrr_flags,
|
||||
@ -431,16 +429,18 @@ static int and_range_trees(RANGE_OPT_PARAM *param,
|
||||
static bool remove_nonrange_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree);
|
||||
|
||||
static void print_key_value(String *out, const KEY_PART_INFO *key_part,
|
||||
const uchar *key);
|
||||
const uchar* key, uint length);
|
||||
static void print_keyparts_name(String *out, const KEY_PART_INFO *key_part,
|
||||
uint n_keypart, key_part_map keypart_map);
|
||||
|
||||
static void append_range_all_keyparts(Json_writer_array *range_trace,
|
||||
String *range_string,
|
||||
String *range_so_far, const SEL_ARG *keypart,
|
||||
const KEY_PART_INFO *key_parts);
|
||||
static void trace_ranges(Json_writer_array *range_trace,
|
||||
PARAM *param, uint idx,
|
||||
SEL_ARG *keypart,
|
||||
const KEY_PART_INFO *key_parts);
|
||||
|
||||
static
|
||||
void append_range(String *out, const KEY_PART_INFO *key_parts,
|
||||
const uchar *min_key, const uchar *max_key, const uint flag);
|
||||
void print_range(String *out, const KEY_PART_INFO *key_part,
|
||||
KEY_MULTI_RANGE *range, uint n_key_parts);
|
||||
|
||||
|
||||
/*
|
||||
@ -2208,7 +2208,7 @@ public:
|
||||
@param param Parameters for range analysis of this table
|
||||
@param trace_object The optimizer trace object the info is appended to
|
||||
*/
|
||||
virtual void trace_basic_info(const PARAM *param,
|
||||
virtual void trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const= 0;
|
||||
|
||||
};
|
||||
@ -2251,11 +2251,11 @@ public:
|
||||
}
|
||||
DBUG_RETURN(quick);
|
||||
}
|
||||
void trace_basic_info(const PARAM *param,
|
||||
void trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const;
|
||||
};
|
||||
|
||||
void TRP_RANGE::trace_basic_info(const PARAM *param,
|
||||
void TRP_RANGE::trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const
|
||||
{
|
||||
DBUG_ASSERT(param->using_real_indexes);
|
||||
@ -2273,10 +2273,7 @@ void TRP_RANGE::trace_basic_info(const PARAM *param,
|
||||
// TRP_RANGE should not be created if there are no range intervals
|
||||
DBUG_ASSERT(key);
|
||||
|
||||
String range_info;
|
||||
range_info.length(0);
|
||||
range_info.set_charset(system_charset_info);
|
||||
append_range_all_keyparts(&trace_range, NULL, &range_info, key, key_part);
|
||||
trace_ranges(&trace_range, param, key_idx, key, key_part);
|
||||
}
|
||||
|
||||
|
||||
@ -2296,7 +2293,7 @@ public:
|
||||
struct st_ror_scan_info *cpk_scan; /* Clustered PK scan, if there is one */
|
||||
bool is_covering; /* TRUE if no row retrieval phase is necessary */
|
||||
double index_scan_costs; /* SUM(cost(index_scan)) */
|
||||
void trace_basic_info(const PARAM *param,
|
||||
void trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const;
|
||||
};
|
||||
|
||||
@ -2317,11 +2314,11 @@ public:
|
||||
MEM_ROOT *parent_alloc);
|
||||
TABLE_READ_PLAN **first_ror; /* array of ptrs to plans for merged scans */
|
||||
TABLE_READ_PLAN **last_ror; /* end of the above array */
|
||||
void trace_basic_info(const PARAM *param,
|
||||
void trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const;
|
||||
};
|
||||
|
||||
void TRP_ROR_UNION::trace_basic_info(const PARAM *param,
|
||||
void TRP_ROR_UNION::trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const
|
||||
{
|
||||
THD *thd= param->thd;
|
||||
@ -2351,12 +2348,12 @@ public:
|
||||
TRP_RANGE **range_scans_end; /* end of the array */
|
||||
/* keys whose scans are to be filtered by cpk conditions */
|
||||
key_map filtered_scans;
|
||||
void trace_basic_info(const PARAM *param,
|
||||
void trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const;
|
||||
|
||||
};
|
||||
|
||||
void TRP_INDEX_INTERSECT::trace_basic_info(const PARAM *param,
|
||||
void TRP_INDEX_INTERSECT::trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const
|
||||
{
|
||||
THD *thd= param->thd;
|
||||
@ -2385,11 +2382,11 @@ public:
|
||||
MEM_ROOT *parent_alloc);
|
||||
TRP_RANGE **range_scans; /* array of ptrs to plans of merged scans */
|
||||
TRP_RANGE **range_scans_end; /* end of the array */
|
||||
void trace_basic_info(const PARAM *param,
|
||||
void trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const;
|
||||
};
|
||||
|
||||
void TRP_INDEX_MERGE::trace_basic_info(const PARAM *param,
|
||||
void TRP_INDEX_MERGE::trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const
|
||||
{
|
||||
THD *thd= param->thd;
|
||||
@ -2452,12 +2449,12 @@ public:
|
||||
QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
|
||||
MEM_ROOT *parent_alloc);
|
||||
void use_index_scan() { is_index_scan= TRUE; }
|
||||
void trace_basic_info(const PARAM *param,
|
||||
void trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const;
|
||||
};
|
||||
|
||||
|
||||
void TRP_GROUP_MIN_MAX::trace_basic_info(const PARAM *param,
|
||||
void TRP_GROUP_MIN_MAX::trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const
|
||||
{
|
||||
THD *thd= param->thd;
|
||||
@ -2489,10 +2486,8 @@ void TRP_GROUP_MIN_MAX::trace_basic_info(const PARAM *param,
|
||||
// can have group quick without ranges
|
||||
if (index_tree)
|
||||
{
|
||||
String range_info;
|
||||
range_info.set_charset(system_charset_info);
|
||||
append_range_all_keyparts(&trace_range, NULL, &range_info, index_tree,
|
||||
key_part);
|
||||
trace_ranges(&trace_range, param, param_idx,
|
||||
index_tree, key_part);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3176,6 +3171,7 @@ double records_in_column_ranges(PARAM *param, uint idx,
|
||||
seq.real_keyno= MAX_KEY;
|
||||
seq.param= param;
|
||||
seq.start= tree;
|
||||
seq.is_ror_scan= FALSE;
|
||||
|
||||
seq_it= seq_if.init((void *) &seq, 0, flags);
|
||||
|
||||
@ -3395,7 +3391,6 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
|
||||
param.mem_root= &alloc;
|
||||
param.old_root= thd->mem_root;
|
||||
param.table= table;
|
||||
param.is_ror_scan= FALSE;
|
||||
param.remove_false_where_parts= true;
|
||||
|
||||
if (create_key_parts_for_pseudo_indexes(¶m, used_fields))
|
||||
@ -6374,7 +6369,7 @@ typedef struct st_ror_scan_info : INDEX_SCAN_INFO
|
||||
{
|
||||
} ROR_SCAN_INFO;
|
||||
|
||||
void TRP_ROR_INTERSECT::trace_basic_info(const PARAM *param,
|
||||
void TRP_ROR_INTERSECT::trace_basic_info(PARAM *param,
|
||||
Json_writer_object *trace_object) const
|
||||
{
|
||||
THD *thd= param->thd;
|
||||
@ -6397,20 +6392,9 @@ void TRP_ROR_INTERSECT::trace_basic_info(const PARAM *param,
|
||||
trace_isect_idx.add("rows", (*cur_scan)->records);
|
||||
|
||||
Json_writer_array trace_range(thd, "ranges");
|
||||
for (const SEL_ARG *current= (*cur_scan)->sel_arg->first(); current;
|
||||
current= current->next)
|
||||
{
|
||||
String range_info;
|
||||
range_info.set_charset(system_charset_info);
|
||||
for (const SEL_ARG *part= current; part;
|
||||
part= part->next_key_part ? part->next_key_part : nullptr)
|
||||
{
|
||||
const KEY_PART_INFO *cur_key_part= key_part + part->part;
|
||||
append_range(&range_info, cur_key_part, part->min_value,
|
||||
part->max_value, part->min_flag | part->max_flag);
|
||||
}
|
||||
trace_range.add(range_info.ptr(), range_info.length());
|
||||
}
|
||||
|
||||
trace_ranges(&trace_range, param, (*cur_scan)->idx,
|
||||
(*cur_scan)->sel_arg, key_part);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7363,6 +7347,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
|
||||
Cost_estimate cost;
|
||||
double found_read_time;
|
||||
uint mrr_flags, buf_size;
|
||||
bool is_ror_scan= FALSE;
|
||||
INDEX_SCAN_INFO *index_scan;
|
||||
uint keynr= param->real_keynr[idx];
|
||||
if (key->type == SEL_ARG::MAYBE_KEY ||
|
||||
@ -7377,7 +7362,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
|
||||
|
||||
found_records= check_quick_select(param, idx, read_index_only, key,
|
||||
update_tbl_stats, &mrr_flags,
|
||||
&buf_size, &cost);
|
||||
&buf_size, &cost, &is_ror_scan);
|
||||
|
||||
if (found_records != HA_POS_ERROR && tree->index_scans &&
|
||||
(index_scan= (INDEX_SCAN_INFO *)alloc_root(param->mem_root,
|
||||
@ -7388,9 +7373,6 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
|
||||
const KEY &cur_key= param->table->key_info[keynr];
|
||||
const KEY_PART_INFO *key_part= cur_key.key_part;
|
||||
|
||||
String range_info;
|
||||
range_info.set_charset(system_charset_info);
|
||||
|
||||
index_scan->idx= idx;
|
||||
index_scan->keynr= keynr;
|
||||
index_scan->key_info= ¶m->table->key_info[keynr];
|
||||
@ -7401,17 +7383,16 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
|
||||
*tree->index_scans_end++= index_scan;
|
||||
|
||||
if (unlikely(thd->trace_started()))
|
||||
append_range_all_keyparts(&trace_range, NULL, &range_info, key,
|
||||
key_part);
|
||||
trace_ranges(&trace_range, param, idx, key, key_part);
|
||||
trace_range.end();
|
||||
|
||||
trace_idx.add("rowid_ordered", param->is_ror_scan)
|
||||
trace_idx.add("rowid_ordered", is_ror_scan)
|
||||
.add("using_mrr", !(mrr_flags & HA_MRR_USE_DEFAULT_IMPL))
|
||||
.add("index_only", read_index_only)
|
||||
.add("rows", found_records)
|
||||
.add("cost", cost.total_cost());
|
||||
}
|
||||
if ((found_records != HA_POS_ERROR) && param->is_ror_scan)
|
||||
if ((found_records != HA_POS_ERROR) && is_ror_scan)
|
||||
{
|
||||
tree->n_ror_scans++;
|
||||
tree->ror_scans_map.set_bit(idx);
|
||||
@ -11026,7 +11007,8 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
|
||||
static
|
||||
ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
|
||||
SEL_ARG *tree, bool update_tbl_stats,
|
||||
uint *mrr_flags, uint *bufsize, Cost_estimate *cost)
|
||||
uint *mrr_flags, uint *bufsize, Cost_estimate *cost,
|
||||
bool *is_ror_scan)
|
||||
{
|
||||
SEL_ARG_RANGE_SEQ seq;
|
||||
RANGE_SEQ_IF seq_if = {NULL, sel_arg_range_seq_init, sel_arg_range_seq_next, 0, 0};
|
||||
@ -11051,9 +11033,9 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
|
||||
param->range_count=0;
|
||||
param->max_key_part=0;
|
||||
|
||||
param->is_ror_scan= TRUE;
|
||||
seq.is_ror_scan= TRUE;
|
||||
if (file->index_flags(keynr, 0, TRUE) & HA_KEY_SCAN_NOT_ROR)
|
||||
param->is_ror_scan= FALSE;
|
||||
seq.is_ror_scan= FALSE;
|
||||
|
||||
*mrr_flags= param->force_default_mrr? HA_MRR_USE_DEFAULT_IMPL: 0;
|
||||
/*
|
||||
@ -11106,12 +11088,12 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
|
||||
TODO: Don't have this logic here, make table engines return
|
||||
appropriate flags instead.
|
||||
*/
|
||||
param->is_ror_scan= FALSE;
|
||||
seq.is_ror_scan= FALSE;
|
||||
}
|
||||
else if (param->table->s->primary_key == keynr && pk_is_clustered)
|
||||
{
|
||||
/* Clustered PK scan is always a ROR scan (TODO: same as above) */
|
||||
param->is_ror_scan= TRUE;
|
||||
seq.is_ror_scan= TRUE;
|
||||
}
|
||||
else if (param->range_count > 1)
|
||||
{
|
||||
@ -11121,8 +11103,9 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
|
||||
(1,3)" returns ROR order for all records with x=1, then ROR
|
||||
order for records with x=3
|
||||
*/
|
||||
param->is_ror_scan= FALSE;
|
||||
seq.is_ror_scan= FALSE;
|
||||
}
|
||||
*is_ror_scan= seq.is_ror_scan;
|
||||
|
||||
DBUG_PRINT("exit", ("Records: %lu", (ulong) rows));
|
||||
DBUG_RETURN(rows); //psergey-merge:todo: maintain first_null_comp.
|
||||
@ -13547,21 +13530,19 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
|
||||
Cost_estimate dummy_cost;
|
||||
uint mrr_flags= HA_MRR_USE_DEFAULT_IMPL;
|
||||
uint mrr_bufsize=0;
|
||||
bool is_ror_scan= FALSE;
|
||||
cur_quick_prefix_records= check_quick_select(param, cur_param_idx,
|
||||
FALSE /*don't care*/,
|
||||
cur_index_tree, TRUE,
|
||||
&mrr_flags, &mrr_bufsize,
|
||||
&dummy_cost);
|
||||
&dummy_cost, &is_ror_scan);
|
||||
if (unlikely(cur_index_tree && thd->trace_started()))
|
||||
{
|
||||
Json_writer_array trace_range(thd, "ranges");
|
||||
|
||||
const KEY_PART_INFO *key_part= cur_index_info->key_part;
|
||||
|
||||
String range_info;
|
||||
range_info.set_charset(system_charset_info);
|
||||
append_range_all_keyparts(&trace_range, NULL, &range_info,
|
||||
cur_index_tree, key_part);
|
||||
trace_ranges(&trace_range, param, cur_param_idx,
|
||||
cur_index_tree, key_part);
|
||||
}
|
||||
}
|
||||
cost_group_min_max(table, cur_index_info, cur_used_key_parts,
|
||||
@ -15733,12 +15714,16 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose)
|
||||
}
|
||||
|
||||
#endif /* !DBUG_OFF */
|
||||
|
||||
static
|
||||
void append_range(String *out, const KEY_PART_INFO *key_part,
|
||||
const uchar *min_key, const uchar *max_key, const uint flag)
|
||||
void print_range(String *out, const KEY_PART_INFO *key_part,
|
||||
KEY_MULTI_RANGE *range, uint n_key_parts)
|
||||
{
|
||||
if (out->length() > 0)
|
||||
out->append(STRING_WITH_LEN(" AND "));
|
||||
uint flag= range->range_flag;
|
||||
String key_name;
|
||||
key_name.set_charset(system_charset_info);
|
||||
key_part_map keypart_map= range->start_key.keypart_map |
|
||||
range->end_key.keypart_map;
|
||||
|
||||
if (flag & GEOM_FLAG)
|
||||
{
|
||||
@ -15747,22 +15732,24 @@ void append_range(String *out, const KEY_PART_INFO *key_part,
|
||||
range types, so printing "col < some_geom" doesn't make sense.
|
||||
Just print the column name, not operator.
|
||||
*/
|
||||
out->append(key_part->field->field_name);
|
||||
print_keyparts_name(out, key_part, n_key_parts, keypart_map);
|
||||
out->append(STRING_WITH_LEN(" "));
|
||||
print_key_value(out, key_part, min_key);
|
||||
print_key_value(out, key_part, range->start_key.key,
|
||||
range->start_key.length);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(flag & NO_MIN_RANGE))
|
||||
{
|
||||
print_key_value(out, key_part, min_key);
|
||||
print_key_value(out, key_part, range->start_key.key,
|
||||
range->start_key.length);
|
||||
if (flag & NEAR_MIN)
|
||||
out->append(STRING_WITH_LEN(" < "));
|
||||
else
|
||||
out->append(STRING_WITH_LEN(" <= "));
|
||||
}
|
||||
|
||||
out->append(key_part->field->field_name);
|
||||
print_keyparts_name(out, key_part, n_key_parts, keypart_map);
|
||||
|
||||
if (!(flag & NO_MAX_RANGE))
|
||||
{
|
||||
@ -15770,7 +15757,8 @@ void append_range(String *out, const KEY_PART_INFO *key_part,
|
||||
out->append(STRING_WITH_LEN(" < "));
|
||||
else
|
||||
out->append(STRING_WITH_LEN(" <= "));
|
||||
print_key_value(out, key_part, max_key);
|
||||
print_key_value(out, key_part, range->end_key.key,
|
||||
range->end_key.length);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15778,60 +15766,43 @@ void append_range(String *out, const KEY_PART_INFO *key_part,
|
||||
|
||||
Add ranges to the trace
|
||||
For ex:
|
||||
query: select * from t1 where a=2 ;
|
||||
and we have an index on a , so we create a range
|
||||
2 <= a <= 2
|
||||
lets say we have an index a_b(a,b)
|
||||
query: select * from t1 where a=2 and b=4 ;
|
||||
so we create a range:
|
||||
(2,4) <= (a,b) <= (2,4)
|
||||
this is added to the trace
|
||||
*/
|
||||
|
||||
static void append_range_all_keyparts(Json_writer_array *range_trace,
|
||||
String *range_string,
|
||||
String *range_so_far, const SEL_ARG *keypart,
|
||||
const KEY_PART_INFO *key_parts)
|
||||
static void trace_ranges(Json_writer_array *range_trace,
|
||||
PARAM *param, uint idx,
|
||||
SEL_ARG *keypart,
|
||||
const KEY_PART_INFO *key_parts)
|
||||
{
|
||||
|
||||
DBUG_ASSERT(keypart);
|
||||
DBUG_ASSERT(keypart && keypart != &null_element);
|
||||
|
||||
// Navigate to first interval in red-black tree
|
||||
SEL_ARG_RANGE_SEQ seq;
|
||||
KEY_MULTI_RANGE range;
|
||||
range_seq_t seq_it;
|
||||
uint flags= 0;
|
||||
RANGE_SEQ_IF seq_if = {NULL, sel_arg_range_seq_init,
|
||||
sel_arg_range_seq_next, 0, 0};
|
||||
KEY *keyinfo= param->table->key_info + param->real_keynr[idx];
|
||||
uint n_key_parts= param->table->actual_n_key_parts(keyinfo);
|
||||
seq.keyno= idx;
|
||||
seq.real_keyno= param->real_keynr[idx];
|
||||
seq.param= param;
|
||||
seq.start= keypart;
|
||||
/*
|
||||
is_ror_scan is set to FALSE here, because we are only interested
|
||||
in iterating over all the ranges and printing them.
|
||||
*/
|
||||
seq.is_ror_scan= FALSE;
|
||||
const KEY_PART_INFO *cur_key_part= key_parts + keypart->part;
|
||||
const SEL_ARG *keypart_range= keypart->first();
|
||||
const size_t save_range_so_far_length= range_so_far->length();
|
||||
seq_it= seq_if.init((void *) &seq, 0, flags);
|
||||
|
||||
|
||||
while (keypart_range)
|
||||
while (!seq_if.next(seq_it, &range))
|
||||
{
|
||||
// Append the current range predicate to the range String
|
||||
switch (keypart->type)
|
||||
{
|
||||
case SEL_ARG::Type::KEY_RANGE:
|
||||
append_range(range_so_far, cur_key_part, keypart_range->min_value,
|
||||
keypart_range->max_value,
|
||||
keypart_range->min_flag | keypart_range->max_flag);
|
||||
break;
|
||||
case SEL_ARG::Type::MAYBE_KEY:
|
||||
range_so_far->append("MAYBE_KEY");
|
||||
break;
|
||||
case SEL_ARG::Type::IMPOSSIBLE:
|
||||
range_so_far->append("IMPOSSIBLE");
|
||||
break;
|
||||
default:
|
||||
DBUG_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (keypart_range->next_key_part &&
|
||||
keypart_range->next_key_part->part ==
|
||||
keypart_range->part + 1 &&
|
||||
keypart_range->is_singlepoint())
|
||||
{
|
||||
append_range_all_keyparts(range_trace, range_string, range_so_far,
|
||||
keypart_range->next_key_part, key_parts);
|
||||
}
|
||||
else
|
||||
range_trace->add(range_so_far->c_ptr_safe(), range_so_far->length());
|
||||
keypart_range= keypart_range->next;
|
||||
range_so_far->length(save_range_so_far_length);
|
||||
StringBuffer<128> range_info(system_charset_info);
|
||||
print_range(&range_info, cur_key_part, &range, n_key_parts);
|
||||
range_trace->add(range_info.c_ptr_safe(), range_info.length());
|
||||
}
|
||||
}
|
||||
|
||||
@ -15841,70 +15812,110 @@ static void append_range_all_keyparts(Json_writer_array *range_trace,
|
||||
@param[out] out String the key is appended to
|
||||
@param[in] key_part Index components description
|
||||
@param[in] key Key tuple
|
||||
@param[in] used_length length of the key tuple
|
||||
*/
|
||||
|
||||
static void print_key_value(String *out, const KEY_PART_INFO *key_part,
|
||||
const uchar *key)
|
||||
const uchar* key, uint used_length)
|
||||
{
|
||||
out->append(STRING_WITH_LEN("("));
|
||||
Field *field= key_part->field;
|
||||
|
||||
if (field->flags & BLOB_FLAG)
|
||||
{
|
||||
// Byte 0 of a nullable key is the null-byte. If set, key is NULL.
|
||||
if (field->real_maybe_null() && *key)
|
||||
out->append(STRING_WITH_LEN("NULL"));
|
||||
else
|
||||
(field->type() == MYSQL_TYPE_GEOMETRY)
|
||||
? out->append(STRING_WITH_LEN("unprintable_geometry_value"))
|
||||
: out->append(STRING_WITH_LEN("unprintable_blob_value"));
|
||||
return;
|
||||
}
|
||||
|
||||
uint store_length= key_part->store_length;
|
||||
|
||||
if (field->real_maybe_null())
|
||||
{
|
||||
/*
|
||||
Byte 0 of key is the null-byte. If set, key is NULL.
|
||||
Otherwise, print the key value starting immediately after the
|
||||
null-byte
|
||||
*/
|
||||
if (*key)
|
||||
{
|
||||
out->append(STRING_WITH_LEN("NULL"));
|
||||
return;
|
||||
}
|
||||
key++; // Skip null byte
|
||||
store_length--;
|
||||
}
|
||||
|
||||
/*
|
||||
Binary data cannot be converted to UTF8 which is what the
|
||||
optimizer trace expects. If the column is binary, the hex
|
||||
representation is printed to the trace instead.
|
||||
*/
|
||||
if (field->flags & BINARY_FLAG)
|
||||
{
|
||||
out->append("0x");
|
||||
for (uint i = 0; i < store_length; i++)
|
||||
{
|
||||
out->append(_dig_vec_lower[*(key + i) >> 4]);
|
||||
out->append(_dig_vec_lower[*(key + i) & 0x0F]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuffer<128> tmp(system_charset_info);
|
||||
TABLE *table= field->table;
|
||||
uint store_length;
|
||||
my_bitmap_map *old_sets[2];
|
||||
|
||||
dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set);
|
||||
const uchar *key_end= key+used_length;
|
||||
|
||||
field->set_key_image(key, key_part->length);
|
||||
if (field->type() == MYSQL_TYPE_BIT)
|
||||
(void)field->val_int_as_str(&tmp, 1); // may change tmp's charset
|
||||
else
|
||||
field->val_str(&tmp); // may change tmp's charset
|
||||
out->append(tmp.ptr(), tmp.length(), tmp.charset());
|
||||
for (; key < key_end; key+=store_length, key_part++)
|
||||
{
|
||||
field= key_part->field;
|
||||
store_length= key_part->store_length;
|
||||
if (field->flags & BLOB_FLAG)
|
||||
{
|
||||
// Byte 0 of a nullable key is the null-byte. If set, key is NULL.
|
||||
if (field->real_maybe_null() && *key)
|
||||
out->append(STRING_WITH_LEN("NULL"));
|
||||
else
|
||||
(field->type() == MYSQL_TYPE_GEOMETRY)
|
||||
? out->append(STRING_WITH_LEN("unprintable_geometry_value"))
|
||||
: out->append(STRING_WITH_LEN("unprintable_blob_value"));
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (field->real_maybe_null())
|
||||
{
|
||||
/*
|
||||
Byte 0 of key is the null-byte. If set, key is NULL.
|
||||
Otherwise, print the key value starting immediately after the
|
||||
null-byte
|
||||
*/
|
||||
if (*key)
|
||||
{
|
||||
out->append(STRING_WITH_LEN("NULL"));
|
||||
goto next;
|
||||
}
|
||||
key++; // Skip null byte
|
||||
store_length--;
|
||||
}
|
||||
|
||||
/*
|
||||
Binary data cannot be converted to UTF8 which is what the
|
||||
optimizer trace expects. If the column is binary, the hex
|
||||
representation is printed to the trace instead.
|
||||
*/
|
||||
if (field->flags & BINARY_FLAG)
|
||||
{
|
||||
out->append("0x");
|
||||
for (uint i = 0; i < store_length; i++)
|
||||
{
|
||||
out->append(_dig_vec_lower[*(key + i) >> 4]);
|
||||
out->append(_dig_vec_lower[*(key + i) & 0x0F]);
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
|
||||
field->set_key_image(key, key_part->length);
|
||||
if (field->type() == MYSQL_TYPE_BIT)
|
||||
(void)field->val_int_as_str(&tmp, 1); // may change tmp's charset
|
||||
else
|
||||
field->val_str(&tmp); // may change tmp's charset
|
||||
out->append(tmp.ptr(), tmp.length(), tmp.charset());
|
||||
|
||||
next:
|
||||
if (key + store_length < key_end)
|
||||
out->append(STRING_WITH_LEN(","));
|
||||
}
|
||||
dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets);
|
||||
out->append(STRING_WITH_LEN(")"));
|
||||
}
|
||||
|
||||
/**
|
||||
Print key parts involed in a range
|
||||
@param[out] out String the key is appended to
|
||||
@param[in] key_part Index components description
|
||||
@param[in] n_keypart Number of keyparts in index
|
||||
@param[in] keypart_map map for keyparts involved in the range
|
||||
*/
|
||||
|
||||
void print_keyparts_name(String *out, const KEY_PART_INFO *key_part,
|
||||
uint n_keypart, key_part_map keypart_map)
|
||||
{
|
||||
uint i;
|
||||
out->append(STRING_WITH_LEN("("));
|
||||
bool first_keypart= TRUE;
|
||||
for (i=0; i < n_keypart; key_part++, i++)
|
||||
{
|
||||
if (keypart_map & (1 << i))
|
||||
{
|
||||
if (first_keypart)
|
||||
first_keypart= FALSE;
|
||||
else
|
||||
out->append(STRING_WITH_LEN(","));
|
||||
out->append(key_part->field->field_name);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
out->append(STRING_WITH_LEN(")"));
|
||||
}
|
||||
|
Reference in New Issue
Block a user