summaryrefslogtreecommitdiff
path: root/sql/opt_range.cc
diff options
context:
space:
mode:
authorMikael Ronstrom <mikael@mysql.com>2009-10-28 18:22:36 +0100
committerMikael Ronstrom <mikael@mysql.com>2009-10-28 18:22:36 +0100
commit072c13d9395b6a3f3a472633d6e817db9215d81e (patch)
tree0891f6df26609fa66b89a4cc8faa8079ec21f6f3 /sql/opt_range.cc
parentcd6e15989044e5d4a708a6ee191bcd3c13dc0071 (diff)
parent10fed1aca0096acb135c2065233e84d61b00b9cf (diff)
downloadmariadb-git-072c13d9395b6a3f3a472633d6e817db9215d81e.tar.gz
Merged WL#3352 into mysql-next-mr
Diffstat (limited to 'sql/opt_range.cc')
-rw-r--r--sql/opt_range.cc331
1 files changed, 255 insertions, 76 deletions
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index e1c7d4b38aa..c2c91881960 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -1,4 +1,4 @@
-/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+/* Copyright 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -438,35 +438,55 @@ public:
return 0;
}
- /* returns a number of keypart values appended to the key buffer */
- int store_min_key(KEY_PART *key, uchar **range_key, uint *range_key_flag)
+ /*
+ Returns a number of keypart values appended to the key buffer
+ for min key and max key. This function is used by both Range
+ Analysis and Partition pruning. For partition pruning we have
+ to ensure that we don't store also subpartition fields. Thus
+ we have to stop at the last partition part and not step into
+ the subpartition fields. For Range Analysis we set last_part
+ to MAX_KEY which we should never reach.
+ */
+ int store_min_key(KEY_PART *key,
+ uchar **range_key,
+ uint *range_key_flag,
+ uint last_part)
{
SEL_ARG *key_tree= first();
uint res= key_tree->store_min(key[key_tree->part].store_length,
range_key, *range_key_flag);
*range_key_flag|= key_tree->min_flag;
if (key_tree->next_key_part &&
+ key_tree->part != last_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
!(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN)) &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
- res+= key_tree->next_key_part->store_min_key(key, range_key,
- range_key_flag);
+ res+= key_tree->next_key_part->store_min_key(key,
+ range_key,
+ range_key_flag,
+ last_part);
return res;
}
/* returns a number of keypart values appended to the key buffer */
- int store_max_key(KEY_PART *key, uchar **range_key, uint *range_key_flag)
+ int store_max_key(KEY_PART *key,
+ uchar **range_key,
+ uint *range_key_flag,
+ uint last_part)
{
SEL_ARG *key_tree= last();
uint res=key_tree->store_max(key[key_tree->part].store_length,
range_key, *range_key_flag);
(*range_key_flag)|= key_tree->max_flag;
if (key_tree->next_key_part &&
+ key_tree->part != last_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
!(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX)) &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
- res+= key_tree->next_key_part->store_max_key(key, range_key,
- range_key_flag);
+ res+= key_tree->next_key_part->store_max_key(key,
+ range_key,
+ range_key_flag,
+ last_part);
return res;
}
@@ -634,6 +654,14 @@ public:
using_real_indexes==TRUE
*/
uint real_keynr[MAX_KEY];
+
+ /*
+ Used to store 'current key tuples', in both range analysis and
+ partitioning (list) analysis
+ */
+ uchar min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
+ max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
+
/* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
uint alloced_sel_args;
};
@@ -645,8 +673,6 @@ public:
longlong baseflag;
uint max_key_part, range_count;
- uchar min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
- max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
bool quick; // Don't calulate possible keys
uint fields_bitmap_size;
@@ -2604,6 +2630,8 @@ typedef struct st_part_prune_param
/* Same as above for subpartitioning */
my_bool *is_subpart_keypart;
+ my_bool ignore_part_fields; /* Ignore rest of partioning fields */
+
/***************************************************************
Following fields form find_used_partitions() recursion context:
**************************************************************/
@@ -2617,8 +2645,13 @@ typedef struct st_part_prune_param
/* Iterator to be used to obtain the "current" set of used partitions */
PARTITION_ITERATOR part_iter;
- /* Initialized bitmap of no_subparts size */
+ /* Initialized bitmap of num_subparts size */
MY_BITMAP subparts_bitmap;
+
+ uchar *cur_min_key;
+ uchar *cur_max_key;
+
+ uint cur_min_flag, cur_max_flag;
} PART_PRUNE_PARAM;
static bool create_partition_index_description(PART_PRUNE_PARAM *prune_par);
@@ -2736,6 +2769,11 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
prune_param.arg_stack_end= prune_param.arg_stack;
prune_param.cur_part_fields= 0;
prune_param.cur_subpart_fields= 0;
+
+ prune_param.cur_min_key= prune_param.range_param.min_key;
+ prune_param.cur_max_key= prune_param.range_param.max_key;
+ prune_param.cur_min_flag= prune_param.cur_max_flag= 0;
+
init_all_partitions_iterator(part_info, &prune_param.part_iter);
if (!tree->keys[0] || (-1 == (res= find_used_partitions(&prune_param,
tree->keys[0]))))
@@ -2873,8 +2911,8 @@ static void mark_full_partition_used_no_parts(partition_info* part_info,
static void mark_full_partition_used_with_parts(partition_info *part_info,
uint32 part_id)
{
- uint32 start= part_id * part_info->no_subparts;
- uint32 end= start + part_info->no_subparts;
+ uint32 start= part_id * part_info->num_subparts;
+ uint32 end= start + part_info->num_subparts;
DBUG_ENTER("mark_full_partition_used_with_parts");
for (; start != end; start++)
@@ -2972,6 +3010,11 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge)
ppar->arg_stack_end= ppar->arg_stack;
ppar->cur_part_fields= 0;
ppar->cur_subpart_fields= 0;
+
+ ppar->cur_min_key= ppar->range_param.min_key;
+ ppar->cur_max_key= ppar->range_param.max_key;
+ ppar->cur_min_flag= ppar->cur_max_flag= 0;
+
init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
SEL_ARG *key_tree= (*ptree)->keys[0];
if (!key_tree || (-1 == (res |= find_used_partitions(ppar, key_tree))))
@@ -3095,9 +3138,13 @@ static
int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
{
int res, left_res=0, right_res=0;
- int partno= (int)key_tree->part;
- bool pushed= FALSE;
+ int key_tree_part= (int)key_tree->part;
bool set_full_part_if_bad_ret= FALSE;
+ bool ignore_part_fields= ppar->ignore_part_fields;
+ bool did_set_ignore_part_fields= FALSE;
+
+ if (check_stack_overrun(ppar->range_param.thd, 3*STACK_MIN_SIZE, NULL))
+ return -1;
if (key_tree->left != &null_element)
{
@@ -3105,40 +3152,159 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
return -1;
}
+ /* Push SEL_ARG's to stack to enable looking backwards as well */
+ ppar->cur_part_fields+= ppar->is_part_keypart[key_tree_part];
+ ppar->cur_subpart_fields+= ppar->is_subpart_keypart[key_tree_part];
+ *(ppar->arg_stack_end++)= key_tree;
+
if (key_tree->type == SEL_ARG::KEY_RANGE)
{
- if (partno == 0 && (NULL != ppar->part_info->get_part_iter_for_interval))
+ if (ppar->part_info->get_part_iter_for_interval &&
+ key_tree->part <= ppar->last_part_partno)
{
- /*
- Partitioning is done by RANGE|INTERVAL(monotonic_expr(fieldX)), and
- we got "const1 CMP fieldX CMP const2" interval <-- psergey-todo: change
+ if (ignore_part_fields)
+ {
+ /*
+ We come here when a condition on the first partitioning
+ fields led to evaluating the partitioning condition
+ (due to finding a condition of the type a < const or
+ b > const). Thus we must ignore the rest of the
+ partitioning fields but we still want to analyse the
+ subpartitioning fields.
+ */
+ if (key_tree->next_key_part)
+ res= find_used_partitions(ppar, key_tree->next_key_part);
+ else
+ res= -1;
+ goto pop_and_go_right;
+ }
+ /* Collect left and right bound, their lengths and flags */
+ uchar *min_key= ppar->cur_min_key;
+ uchar *max_key= ppar->cur_max_key;
+ uchar *tmp_min_key= min_key;
+ uchar *tmp_max_key= max_key;
+ key_tree->store_min(ppar->key[key_tree->part].store_length,
+ &tmp_min_key, ppar->cur_min_flag);
+ key_tree->store_max(ppar->key[key_tree->part].store_length,
+ &tmp_max_key, ppar->cur_max_flag);
+ uint flag;
+ if (key_tree->next_key_part &&
+ key_tree->next_key_part->part == key_tree->part+1 &&
+ key_tree->next_key_part->part <= ppar->last_part_partno &&
+ key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
+ {
+ /*
+ There are more key parts for partition pruning to handle
+ This mainly happens when the condition is an equality
+ condition.
+ */
+ if ((tmp_min_key - min_key) == (tmp_max_key - max_key) &&
+ (memcmp(min_key, max_key, (uint)(tmp_max_key - max_key)) == 0) &&
+ !key_tree->min_flag && !key_tree->max_flag)
+ {
+ /* Set 'parameters' */
+ ppar->cur_min_key= tmp_min_key;
+ ppar->cur_max_key= tmp_max_key;
+ uint save_min_flag= ppar->cur_min_flag;
+ uint save_max_flag= ppar->cur_max_flag;
+
+ ppar->cur_min_flag|= key_tree->min_flag;
+ ppar->cur_max_flag|= key_tree->max_flag;
+
+ res= find_used_partitions(ppar, key_tree->next_key_part);
+
+ /* Restore 'parameters' back */
+ ppar->cur_min_key= min_key;
+ ppar->cur_max_key= max_key;
+
+ ppar->cur_min_flag= save_min_flag;
+ ppar->cur_max_flag= save_max_flag;
+ goto pop_and_go_right;
+ }
+ /* We have arrived at the last field in the partition pruning */
+ uint tmp_min_flag= key_tree->min_flag,
+ tmp_max_flag= key_tree->max_flag;
+ if (!tmp_min_flag)
+ key_tree->next_key_part->store_min_key(ppar->key,
+ &tmp_min_key,
+ &tmp_min_flag,
+ ppar->last_part_partno);
+ if (!tmp_max_flag)
+ key_tree->next_key_part->store_max_key(ppar->key,
+ &tmp_max_key,
+ &tmp_max_flag,
+ ppar->last_part_partno);
+ flag= tmp_min_flag | tmp_max_flag;
+ }
+ else
+ flag= key_tree->min_flag | key_tree->max_flag;
+
+ if (tmp_min_key != ppar->range_param.min_key)
+ flag&= ~NO_MIN_RANGE;
+ else
+ flag|= NO_MIN_RANGE;
+ if (tmp_max_key != ppar->range_param.max_key)
+ flag&= ~NO_MAX_RANGE;
+ else
+ flag|= NO_MAX_RANGE;
+
+ /*
+ We need to call the interval mapper if we have a condition which
+ makes sense to prune on. In the example of a COLUMN_LIST on a and
+ b it makes sense if we have a condition on a, or conditions on
+ both a and b. If we only have conditions on b it might make sense
+ but this is a harder case we will solve later. For the harder case
+ this clause then turns into use of all partitions and thus we
+ simply set res= -1 as if the mapper had returned that.
+ TODO: What to do here is defined in WL#4065.
*/
- DBUG_EXECUTE("info", dbug_print_segment_range(key_tree,
- ppar->range_param.
- key_parts););
- res= ppar->part_info->
- get_part_iter_for_interval(ppar->part_info,
- FALSE,
- key_tree->min_value,
- key_tree->max_value,
- key_tree->min_flag | key_tree->max_flag,
- &ppar->part_iter);
- if (!res)
- goto go_right; /* res==0 --> no satisfying partitions */
+ if (ppar->arg_stack[0]->part == 0)
+ {
+ uint32 i;
+ uint32 store_length_array[MAX_KEY];
+ uint32 num_keys= ppar->part_fields;
+
+ for (i= 0; i < num_keys; i++)
+ store_length_array[i]= ppar->key[i].store_length;
+ res= ppar->part_info->
+ get_part_iter_for_interval(ppar->part_info,
+ FALSE,
+ store_length_array,
+ ppar->range_param.min_key,
+ ppar->range_param.max_key,
+ tmp_min_key - ppar->range_param.min_key,
+ tmp_max_key - ppar->range_param.max_key,
+ flag,
+ &ppar->part_iter);
+ if (!res)
+ goto pop_and_go_right; /* res==0 --> no satisfying partitions */
+ }
+ else
+ res= -1;
+
if (res == -1)
{
- //get a full range iterator
+ /* get a full range iterator */
init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
}
/*
Save our intent to mark full partition as used if we will not be able
to obtain further limits on subpartitions
*/
+ if (key_tree_part < ppar->last_part_partno)
+ {
+ /*
+ We need to ignore the rest of the partitioning fields in all
+ evaluations after this
+ */
+ did_set_ignore_part_fields= TRUE;
+ ppar->ignore_part_fields= TRUE;
+ }
set_full_part_if_bad_ret= TRUE;
goto process_next_key_part;
}
- if (partno == ppar->last_subpart_partno &&
+ if (key_tree_part == ppar->last_subpart_partno &&
(NULL != ppar->part_info->get_subpart_iter_for_interval))
{
PARTITION_ITERATOR subpart_iter;
@@ -3148,13 +3314,16 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
res= ppar->part_info->
get_subpart_iter_for_interval(ppar->part_info,
TRUE,
+ NULL, /* Currently not used here */
key_tree->min_value,
key_tree->max_value,
- key_tree->min_flag | key_tree->max_flag,
+ 0, 0, /* Those are ignored here */
+ key_tree->min_flag |
+ key_tree->max_flag,
&subpart_iter);
DBUG_ASSERT(res); /* We can't get "no satisfying subpartitions" */
if (res == -1)
- return -1; /* all subpartitions satisfy */
+ goto pop_and_go_right; /* all subpartitions satisfy */
uint32 subpart_id;
bitmap_clear_all(&ppar->subparts_bitmap);
@@ -3167,23 +3336,19 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) !=
NOT_A_PARTITION_ID)
{
- for (uint i= 0; i < ppar->part_info->no_subparts; i++)
+ for (uint i= 0; i < ppar->part_info->num_subparts; i++)
if (bitmap_is_set(&ppar->subparts_bitmap, i))
bitmap_set_bit(&ppar->part_info->used_partitions,
- part_id * ppar->part_info->no_subparts + i);
+ part_id * ppar->part_info->num_subparts + i);
}
- goto go_right;
+ goto pop_and_go_right;
}
if (key_tree->is_singlepoint())
{
- pushed= TRUE;
- ppar->cur_part_fields+= ppar->is_part_keypart[partno];
- ppar->cur_subpart_fields+= ppar->is_subpart_keypart[partno];
- *(ppar->arg_stack_end++) = key_tree;
-
- if (partno == ppar->last_part_partno &&
- ppar->cur_part_fields == ppar->part_fields)
+ if (key_tree_part == ppar->last_part_partno &&
+ ppar->cur_part_fields == ppar->part_fields &&
+ ppar->part_info->get_part_iter_for_interval == NULL)
{
/*
Ok, we've got "fieldN<=>constN"-type SEL_ARGs for all partitioning
@@ -3212,7 +3377,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
goto process_next_key_part;
}
- if (partno == ppar->last_subpart_partno &&
+ if (key_tree_part == ppar->last_subpart_partno &&
ppar->cur_subpart_fields == ppar->subpart_fields)
{
/*
@@ -3236,7 +3401,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
NOT_A_PARTITION_ID)
{
bitmap_set_bit(&part_info->used_partitions,
- part_id * part_info->no_subparts + subpart_id);
+ part_id * part_info->num_subparts + subpart_id);
}
res= 1; /* Some partitions were marked as used */
goto pop_and_go_right;
@@ -3249,8 +3414,11 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
we're processing subpartititoning's key parts, this means we'll not be
able to infer any suitable condition, so bail out.
*/
- if (partno >= ppar->last_part_partno)
- return -1;
+ if (key_tree_part >= ppar->last_part_partno)
+ {
+ res= -1;
+ goto pop_and_go_right;
+ }
}
}
@@ -3259,7 +3427,17 @@ process_next_key_part:
res= find_used_partitions(ppar, key_tree->next_key_part);
else
res= -1;
-
+
+ if (did_set_ignore_part_fields)
+ {
+ /*
+ We have returned from processing all key trees linked to our next
+ key part. We are ready to be moving down (using right pointers) and
+ this tree is a new evaluation requiring its own decision on whether
+ to ignore partitioning fields.
+ */
+ ppar->ignore_part_fields= FALSE;
+ }
if (set_full_part_if_bad_ret)
{
if (res == -1)
@@ -3282,18 +3460,14 @@ process_next_key_part:
init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
}
- if (pushed)
- {
pop_and_go_right:
- /* Pop this key part info off the "stack" */
- ppar->arg_stack_end--;
- ppar->cur_part_fields-= ppar->is_part_keypart[partno];
- ppar->cur_subpart_fields-= ppar->is_subpart_keypart[partno];
- }
+ /* Pop this key part info off the "stack" */
+ ppar->arg_stack_end--;
+ ppar->cur_part_fields-= ppar->is_part_keypart[key_tree_part];
+ ppar->cur_subpart_fields-= ppar->is_subpart_keypart[key_tree_part];
if (res == -1)
return -1;
-go_right:
if (key_tree->right != &null_element)
{
if (-1 == (right_res= find_used_partitions(ppar,key_tree->right)))
@@ -3375,13 +3549,14 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
uint used_part_fields, used_subpart_fields;
used_part_fields= fields_ok_for_partition_index(part_info->part_field_array) ?
- part_info->no_part_fields : 0;
+ part_info->num_part_fields : 0;
used_subpart_fields=
fields_ok_for_partition_index(part_info->subpart_field_array)?
- part_info->no_subpart_fields : 0;
+ part_info->num_subpart_fields : 0;
uint total_parts= used_part_fields + used_subpart_fields;
+ ppar->ignore_part_fields= FALSE;
ppar->part_fields= used_part_fields;
ppar->last_part_partno= (int)used_part_fields - 1;
@@ -3416,10 +3591,10 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
if (ppar->subpart_fields)
{
my_bitmap_map *buf;
- uint32 bufsize= bitmap_buffer_size(ppar->part_info->no_subparts);
+ uint32 bufsize= bitmap_buffer_size(ppar->part_info->num_subparts);
if (!(buf= (my_bitmap_map*) alloc_root(alloc, bufsize)))
return TRUE;
- bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->no_subparts,
+ bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->num_subparts,
FALSE);
}
range_par->key_parts= key_part;
@@ -3430,12 +3605,8 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
{
key_part->key= 0;
key_part->part= part;
- key_part->store_length= key_part->length= (uint16) (*field)->key_length();
- if ((*field)->real_maybe_null())
- key_part->store_length+= HA_KEY_NULL_LENGTH;
- if ((*field)->type() == MYSQL_TYPE_BLOB ||
- (*field)->real_type() == MYSQL_TYPE_VARCHAR)
- key_part->store_length+= HA_KEY_BLOB_LENGTH;
+ key_part->length= (uint16)get_partition_field_store_length(*field);
+ key_part->store_length= key_part->length;
DBUG_PRINT("info", ("part %u length %u store_length %u", part,
key_part->length, key_part->store_length));
@@ -7553,12 +7724,16 @@ check_quick_keys(PARAM *param, uint idx, SEL_ARG *key_tree,
tmp_max_flag=key_tree->max_flag;
if (!tmp_min_flag)
tmp_min_keypart+=
- key_tree->next_key_part->store_min_key(param->key[idx], &tmp_min_key,
- &tmp_min_flag);
+ key_tree->next_key_part->store_min_key(param->key[idx],
+ &tmp_min_key,
+ &tmp_min_flag,
+ MAX_KEY);
if (!tmp_max_flag)
tmp_max_keypart+=
- key_tree->next_key_part->store_max_key(param->key[idx], &tmp_max_key,
- &tmp_max_flag);
+ key_tree->next_key_part->store_max_key(param->key[idx],
+ &tmp_max_key,
+ &tmp_max_flag,
+ MAX_KEY);
min_key_length= (uint) (tmp_min_key - param->min_key);
max_key_length= (uint) (tmp_max_key - param->max_key);
}
@@ -7828,11 +8003,15 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
{
uint tmp_min_flag=key_tree->min_flag,tmp_max_flag=key_tree->max_flag;
if (!tmp_min_flag)
- min_part+= key_tree->next_key_part->store_min_key(key, &tmp_min_key,
- &tmp_min_flag);
+ min_part+= key_tree->next_key_part->store_min_key(key,
+ &tmp_min_key,
+ &tmp_min_flag,
+ MAX_KEY);
if (!tmp_max_flag)
- max_part+= key_tree->next_key_part->store_max_key(key, &tmp_max_key,
- &tmp_max_flag);
+ max_part+= key_tree->next_key_part->store_max_key(key,
+ &tmp_max_key,
+ &tmp_max_flag,
+ MAX_KEY);
flag=tmp_min_flag | tmp_max_flag;
}
}