summaryrefslogtreecommitdiff
path: root/sql/opt_range.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/opt_range.cc')
-rw-r--r--sql/opt_range.cc101
1 files changed, 87 insertions, 14 deletions
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 2616e044025..b795411130d 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -829,6 +829,12 @@ public:
*/
bool remove_jump_scans;
+ /*
+ TRUE <=> Range analyzer should remove parts of condition that are found
+ to be always FALSE.
+ */
+ bool remove_false_where_parts;
+
/*
used_key_no -> table_key_no translation table. Only makes sense if
using_real_indexes==TRUE
@@ -908,7 +914,7 @@ static SEL_TREE * get_mm_parts(RANGE_OPT_PARAM *param,COND *cond_func,Field *fie
static SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param,COND *cond_func,Field *field,
KEY_PART *key_part,
Item_func::Functype type,Item *value);
-static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond);
+static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond);
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,
@@ -2941,7 +2947,8 @@ static int fill_used_fields_bitmap(PARAM *param)
int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
table_map prev_tables,
ha_rows limit, bool force_quick_range,
- bool ordered_output)
+ bool ordered_output,
+ bool remove_false_parts_of_where)
{
uint idx;
double scan_time;
@@ -3000,6 +3007,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.imerge_cost_buff_size= 0;
param.using_real_indexes= TRUE;
param.remove_jump_scans= TRUE;
+ param.remove_false_where_parts= remove_false_parts_of_where;
param.force_default_mrr= ordered_output;
param.possible_keys.clear_all();
@@ -3073,7 +3081,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
if (cond)
{
- if ((tree= get_mm_tree(&param,cond)))
+ if ((tree= get_mm_tree(&param, &cond)))
{
if (tree->type == SEL_TREE::IMPOSSIBLE)
{
@@ -3415,7 +3423,7 @@ double records_in_column_ranges(PARAM *param, uint idx,
TRUE otherwise
*/
-bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond)
+bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
{
uint keynr;
uint max_quick_key_parts= 0;
@@ -3425,7 +3433,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond)
table->cond_selectivity= 1.0;
- if (!cond || table_records == 0)
+ if (!*cond || table_records == 0)
DBUG_RETURN(FALSE);
if (table->pos_in_table_list->schema_table)
@@ -3529,6 +3537,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond)
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(&param, used_fields))
goto free_alloc;
@@ -3606,7 +3615,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond)
ulong check_rows=
MY_MIN(thd->variables.optimizer_selectivity_sampling_limit,
(ulong) (table_records * SELECTIVITY_SAMPLING_SHARE));
- if (cond && check_rows > SELECTIVITY_SAMPLING_THRESHOLD &&
+ if (*cond && check_rows > SELECTIVITY_SAMPLING_THRESHOLD &&
thd->variables.optimizer_use_condition_selectivity > 4)
{
find_selective_predicates_list_processor_data *dt=
@@ -3617,8 +3626,8 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond)
DBUG_RETURN(TRUE);
dt->list.empty();
dt->table= table;
- if (cond->walk(&Item::find_selective_predicates_list_processor, 0,
- (uchar*) dt))
+ if ((*cond)->walk(&Item::find_selective_predicates_list_processor, 0,
+ (uchar*) dt))
DBUG_RETURN(TRUE);
if (dt->list.elements > 0)
{
@@ -3951,6 +3960,8 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
/* range_par->cond doesn't need initialization */
range_par->prev_tables= range_par->read_tables= 0;
range_par->current_table= table->map;
+ /* It should be possible to switch the following ON: */
+ range_par->remove_false_where_parts= false;
range_par->keys= 1; // one index
range_par->using_real_indexes= FALSE;
@@ -3967,7 +3978,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
SEL_TREE *tree;
int res;
- tree= get_mm_tree(range_par, pprune_cond);
+ tree= get_mm_tree(range_par, &pprune_cond);
if (!tree)
goto all_used;
@@ -7855,15 +7866,33 @@ static SEL_TREE *get_full_func_mm_tree(RANGE_OPT_PARAM *param,
DBUG_RETURN(ftree);
}
- /* make a select tree of all keys in condition */
+/*
+ make a select tree of all keys in condition
+
+ @param param Context
+ @param cond INOUT condition to perform range analysis on.
+
+ @detail
+ Range analysis may infer that some conditions are never true.
+ - If the condition is never true, SEL_TREE(type=IMPOSSIBLE) is returned
+ - if parts of condition are never true, the function may remove these parts
+ from the condition 'cond'. Sometimes, this will cause the condition to
+ be substituted for something else.
+
-static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond)
+ @return
+ NULL - Could not infer anything from condition cond.
+ SEL_TREE with type=IMPOSSIBLE - condition can never be true.
+*/
+
+static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
{
SEL_TREE *tree=0;
SEL_TREE *ftree= 0;
Item_field *field_item= 0;
bool inv= FALSE;
Item *value= 0;
+ Item *cond= *cond_ptr;
DBUG_ENTER("get_mm_tree");
if (cond->type() == Item::COND_ITEM)
@@ -7876,31 +7905,75 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond)
Item *item;
while ((item=li++))
{
- SEL_TREE *new_tree= get_mm_tree(param,item);
+ SEL_TREE *new_tree= get_mm_tree(param,li.ref());
if (param->statement_should_be_aborted())
DBUG_RETURN(NULL);
tree= tree_and(param,tree,new_tree);
if (tree && tree->type == SEL_TREE::IMPOSSIBLE)
+ {
+ /*
+ Do not remove 'item' from 'cond'. We return a SEL_TREE::IMPOSSIBLE
+ and that is sufficient for the caller to see that the whole
+ condition is never true.
+ */
break;
+ }
}
}
else
{ // COND OR
- tree= get_mm_tree(param,li++);
+ bool replace_cond= false;
+ Item *replacement_item= li++;
+ tree= get_mm_tree(param, li.ref());
if (param->statement_should_be_aborted())
DBUG_RETURN(NULL);
if (tree)
{
+ if (tree->type == SEL_TREE::IMPOSSIBLE &&
+ param->remove_false_where_parts)
+ {
+ /* See the other li.remove() call below */
+ li.remove();
+ if (((Item_cond*)cond)->argument_list()->elements <= 1)
+ replace_cond= true;
+ }
+
Item *item;
while ((item=li++))
{
- SEL_TREE *new_tree=get_mm_tree(param,item);
+ SEL_TREE *new_tree=get_mm_tree(param,li.ref());
if (new_tree == NULL || param->statement_should_be_aborted())
DBUG_RETURN(NULL);
tree= tree_or(param,tree,new_tree);
if (tree == NULL || tree->type == SEL_TREE::ALWAYS)
+ {
+ replacement_item= *li.ref();
break;
+ }
+
+ if (new_tree && new_tree->type == SEL_TREE::IMPOSSIBLE &&
+ param->remove_false_where_parts)
+ {
+ /*
+ This is a condition in form
+
+ cond = item1 OR ... OR item_i OR ... itemN
+
+ and item_i produces SEL_TREE(IMPOSSIBLE). We should remove item_i
+ from cond. This may cause 'cond' to become a degenerate,
+ one-way OR. In that case, we replace 'cond' with the remaining
+ item_i.
+ */
+ li.remove();
+ if (((Item_cond*)cond)->argument_list()->elements <= 1)
+ replace_cond= true;
+ }
+ else
+ replacement_item= *li.ref();
}
+
+ if (replace_cond)
+ *cond_ptr= replacement_item;
}
}
DBUG_RETURN(tree);