summaryrefslogtreecommitdiff
path: root/sql/opt_sum.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/opt_sum.cc')
-rw-r--r--sql/opt_sum.cc295
1 files changed, 175 insertions, 120 deletions
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index f8603f06fa0..3ccc1e5cf41 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -14,7 +14,9 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
+/**
+ @file
+
Optimising of MIN(), MAX() and COUNT(*) queries without 'group by' clause
by replacing the aggregate expression with a constant.
@@ -22,6 +24,7 @@
types of queries are optimised (assuming the table handler supports
the required methods)
+ @verbatim
SELECT COUNT(*) FROM t1[,t2,t3,...]
SELECT MIN(b) FROM t1 WHERE a=const
SELECT MAX(c) FROM t1 WHERE a=const AND b=const
@@ -29,6 +32,7 @@
SELECT MIN(b) FROM t1 WHERE a=const AND b>const
SELECT MIN(b) FROM t1 WHERE a=const AND b BETWEEN const AND const
SELECT MAX(b) FROM t1 WHERE a=const AND b BETWEEN const AND const
+ @endverbatim
Instead of '<' one can use '<=', '>', '>=' and '=' as well.
Instead of 'a=const' the condition 'a IS NULL' can be used.
@@ -36,10 +40,11 @@
If all selected fields are replaced then we will also remove all
involved tables and return the answer without any join. Thus, the
following query will be replaced with a row of two constants:
+ @verbatim
SELECT MAX(b), MIN(d) FROM t1,t2
WHERE a=const AND b<const AND d>const
+ @endverbatim
(assuming a index for column d of table t2 is defined)
-
*/
#include "mysql_priv.h"
@@ -54,24 +59,54 @@ static int maxmin_in_range(bool max_fl, Field* field, COND *cond);
/*
- Substitutes constants for some COUNT(), MIN() and MAX() functions.
+ Get exact count of rows in all tables
SYNOPSIS
- opt_sum_query()
- tables list of leaves of join table tree
- all_fields All fields to be returned
- conds WHERE clause
+ get_exact_records()
+ tables List of tables
+
+ NOTES
+ When this is called, we know all table handlers supports HA_HAS_RECORDS
+ or HA_STATS_RECORDS_IS_EXACT
+
+ RETURN
+ ULONGLONG_MAX Error: Could not calculate number of rows
+ # Multiplication of number of rows in all tables
+*/
+
+static ulonglong get_exact_record_count(TABLE_LIST *tables)
+{
+ ulonglong count= 1;
+ for (TABLE_LIST *tl= tables; tl; tl= tl->next_leaf)
+ {
+ ha_rows tmp= tl->table->file->records();
+ if ((tmp == HA_POS_ERROR))
+ return ULONGLONG_MAX;
+ count*= tmp;
+ }
+ return count;
+}
+
+
+/**
+ Substitutes constants for some COUNT(), MIN() and MAX() functions.
- NOTE:
+ @param tables list of leaves of join table tree
+ @param all_fields All fields to be returned
+ @param conds WHERE clause
+
+ @note
This function is only called for queries with sum functions and no
GROUP BY part.
- RETURN VALUES
+ @retval
0 no errors
+ @retval
1 if all items were resolved
+ @retval
HA_ERR_KEY_NOT_FOUND on impossible conditions
- OR an error number from my_base.h HA_ERR_... if a deadlock or a lock
- wait timeout happens, for example
+ @retval
+ HA_ERR_... if a deadlock or a lock wait timeout happens, for example
*/
int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
@@ -79,8 +114,8 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
List_iterator_fast<Item> it(all_fields);
int const_result= 1;
bool recalc_const_item= 0;
- longlong count= 1;
- bool is_exact_count= TRUE;
+ ulonglong count= 1;
+ bool is_exact_count= TRUE, maybe_exact_count= TRUE;
table_map removed_tables= 0, outer_tables= 0, used_tables= 0;
table_map where_tables= 0;
Item *item;
@@ -119,15 +154,18 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
used_tables|= tl->table->map;
/*
- If the storage manager of 'tl' gives exact row count, compute the total
- number of rows. If there are no outer table dependencies, this count
- may be used as the real count.
+ If the storage manager of 'tl' gives exact row count as part of
+ statistics (cheap), compute the total number of rows. If there are
+ no outer table dependencies, this count may be used as the real count.
Schema tables are filled after this function is invoked, so we can't
get row count
*/
- if ((tl->table->file->table_flags() & HA_NOT_EXACT_COUNT) ||
+ if (!(tl->table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) ||
tl->schema_table)
{
+ maybe_exact_count&= test(!tl->schema_table &&
+ (tl->table->file->ha_table_flags() &
+ HA_HAS_RECORDS));
is_exact_count= FALSE;
count= 1; // ensure count != 0
}
@@ -137,9 +175,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
if(error)
{
tl->table->file->print_error(error, MYF(0));
+ tl->table->in_use->fatal_error();
return error;
}
- count*= tl->table->file->records;
+ count*= tl->table->file->stats.records;
}
}
@@ -161,9 +200,19 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
there are no outer joins.
*/
if (!conds && !((Item_sum_count*) item)->get_arg(0)->maybe_null &&
- !outer_tables && is_exact_count)
+ !outer_tables && maybe_exact_count)
{
- ((Item_sum_count*) item)->make_const(count);
+ if (!is_exact_count)
+ {
+ if ((count= get_exact_record_count(tables)) == ULONGLONG_MAX)
+ {
+ /* Error from handler in counting rows. Don't optimize count() */
+ const_result= 0;
+ continue;
+ }
+ is_exact_count= 1; // count is now exact
+ }
+ ((Item_sum_count*) item)->make_const((longlong) count);
recalc_const_item= 1;
}
else
@@ -179,7 +228,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
Item *expr=item_sum->get_arg(0);
if (expr->real_item()->type() == Item::FIELD_ITEM)
{
- byte key_buff[MAX_KEY_LENGTH];
+ uchar key_buff[MAX_KEY_LENGTH];
TABLE_REF ref;
uint range_fl, prefix_len;
@@ -202,7 +251,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
const_result= 0;
break;
}
- error= table->file->ha_index_init((uint) ref.key);
+ error= table->file->ha_index_init((uint) ref.key, 1);
if (!ref.key_length)
error= table->file->index_first(table->record[0]);
@@ -227,9 +276,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
Closed interval: Either The MIN argument is non-nullable, or
we have a >= predicate for the MIN argument.
*/
- error= table->file->index_read(table->record[0], ref.key_buff,
- ref.key_length,
- HA_READ_KEY_OR_NEXT);
+ error= table->file->index_read_map(table->record[0],
+ ref.key_buff,
+ make_prev_keypart_map(ref.key_parts),
+ HA_READ_KEY_OR_NEXT);
else
{
/*
@@ -238,8 +288,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
2) there is a > predicate on it, nullability is irrelevant.
We need to scan the next bigger record first.
*/
- error= table->file->index_read(table->record[0], ref.key_buff,
- ref.key_length, HA_READ_AFTER_KEY);
+ error= table->file->index_read_map(table->record[0],
+ ref.key_buff,
+ make_prev_keypart_map(ref.key_parts),
+ HA_READ_AFTER_KEY);
/*
If the found record is outside the group formed by the search
prefix, or there is no such record at all, check if all
@@ -261,9 +313,11 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
(error == HA_ERR_KEY_NOT_FOUND ||
key_cmp_if_same(table, ref.key_buff, ref.key, prefix_len)))
{
- error= table->file->index_read(table->record[0], ref.key_buff,
- ref.key_length,
- HA_READ_KEY_EXACT);
+ DBUG_ASSERT(item_field->field->real_maybe_null());
+ error= table->file->index_read_map(table->record[0],
+ ref.key_buff,
+ make_prev_keypart_map(ref.key_parts),
+ HA_READ_KEY_EXACT);
}
}
}
@@ -322,7 +376,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
Item *expr=item_sum->get_arg(0);
if (expr->real_item()->type() == Item::FIELD_ITEM)
{
- byte key_buff[MAX_KEY_LENGTH];
+ uchar key_buff[MAX_KEY_LENGTH];
TABLE_REF ref;
uint range_fl, prefix_len;
@@ -345,17 +399,17 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
const_result= 0;
break;
}
- error= table->file->ha_index_init((uint) ref.key);
+ error= table->file->ha_index_init((uint) ref.key, 1);
if (!ref.key_length)
error= table->file->index_last(table->record[0]);
else
- error= table->file->index_read(table->record[0], key_buff,
- ref.key_length,
- range_fl & NEAR_MAX ?
- HA_READ_BEFORE_KEY :
- HA_READ_PREFIX_LAST_OR_PREV);
- if (!error && reckey_in_range(1, &ref, item_field->field,
+ error= table->file->index_read_map(table->record[0], key_buff,
+ make_prev_keypart_map(ref.key_parts),
+ range_fl & NEAR_MAX ?
+ HA_READ_BEFORE_KEY :
+ HA_READ_PREFIX_LAST_OR_PREV);
+ if (!error && reckey_in_range(1, &ref, item_field->field,
conds, range_fl, prefix_len))
error= HA_ERR_KEY_NOT_FOUND;
if (table->key_read)
@@ -370,6 +424,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
return HA_ERR_KEY_NOT_FOUND; // No rows matching WHERE
/* HA_ERR_LOCK_DEADLOCK or some other error */
table->file->print_error(error, MYF(0));
+ table->in_use->fatal_error();
return(error);
}
removed_tables|= table->map;
@@ -425,19 +480,18 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
}
-/*
- Test if the predicate compares a field with constants
+/**
+ Test if the predicate compares a field with constants.
- SYNOPSIS
- simple_pred()
- func_item Predicate item
- args out: Here we store the field followed by constants
- inv_order out: Is set to 1 if the predicate is of the form
- 'const op field'
+ @param func_item Predicate item
+ @param[out] args Here we store the field followed by constants
+ @param[out] inv_order Is set to 1 if the predicate is of the form
+ 'const op field'
- RETURN
+ @retval
0 func_item is a simple predicate: a field is compared with
- constants
+ constants
+ @retval
1 Otherwise
*/
@@ -509,34 +563,33 @@ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order)
}
-/*
- Check whether a condition matches a key to get {MAX|MIN}(field):
+/**
+ Check whether a condition matches a key to get {MAX|MIN}(field):.
- SYNOPSIS
- matching_cond()
- max_fl in: Set to 1 if we are optimising MAX()
- ref in/out: Reference to the structure we store the key value
- keyinfo in Reference to the key info
- field_part in: Pointer to the key part for the field
- cond in WHERE condition
- key_part_used in/out: Map of matchings parts
- range_fl in/out: Says whether including key will be used
- prefix_len out: Length of common key part for the range
- where MAX/MIN is searched for
-
- DESCRIPTION
For the index specified by the keyinfo parameter, index that
contains field as its component (field_part), the function
checks whether the condition cond is a conjunction and all its
conjuncts referring to the columns of the same table as column
field are one of the following forms:
- f_i= const_i or const_i= f_i or f_i is null,
- where f_i is part of the index
+ where f_i is part of the index
- field {<|<=|>=|>|=} const or const {<|<=|>=|>|=} field
- field between const1 and const2
- RETURN
+ @param[in] max_fl Set to 1 if we are optimising MAX()
+ @param[in,out] ref Reference to the structure we store the key
+ value
+ @param[in] keyinfo Reference to the key info
+ @param[in] field_part Pointer to the key part for the field
+ @param[in] cond WHERE condition
+ @param[in,out] key_part_used Map of matchings parts
+ @param[in,out] range_fl Says whether including key will be used
+ @param[out] prefix_len Length of common key part for the range
+ where MAX/MIN is searched for
+
+ @retval
0 Index can't be used.
+ @retval
1 We can use index to get MIN/MAX value
*/
@@ -616,17 +669,15 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
less_fl= 1-less_fl; // Convert '<' -> '>' (etc)
/* Check if field is part of the tested partial key */
- byte *key_ptr= ref->key_buff;
+ uchar *key_ptr= ref->key_buff;
KEY_PART_INFO *part;
- for (part= keyinfo->key_part;
- ;
- key_ptr+= part++->store_length)
+ for (part= keyinfo->key_part; ; key_ptr+= part++->store_length)
{
if (part > field_part)
return 0; // Field is beyond the tested parts
if (part->field->eq(((Item_field*) args[0])->field))
- break; // Found a part od the key for the field
+ break; // Found a part of the key for the field
}
bool is_field_part= part == field_part;
@@ -636,12 +687,15 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
key_part_map org_key_part_used= *key_part_used;
if (eq_type || between || max_fl == less_fl)
{
- size_t length= (key_ptr-ref->key_buff)+part->store_length;
+ uint length= (key_ptr-ref->key_buff)+part->store_length;
if (ref->key_length < length)
+ {
/* Ultimately ref->key_length will contain the length of the search key */
- ref->key_length= (uint) length;
+ ref->key_length= length;
+ ref->key_parts= (part - keyinfo->key_part) + 1;
+ }
if (!*prefix_len && part+1 == field_part)
- *prefix_len= (uint) length;
+ *prefix_len= length;
if (is_field_part && eq_type)
*prefix_len= ref->key_length;
@@ -664,15 +718,15 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
if (is_null)
{
part->field->set_null();
- *key_ptr= (byte) 1;
+ *key_ptr= (uchar) 1;
}
else
{
store_val_in_field(part->field, args[between && max_fl ? 2 : 1],
CHECK_FIELD_IGNORE);
if (part->null_bit)
- *key_ptr++= (byte) test(part->field->is_null());
- part->field->get_key_image((char*) key_ptr, part->length, Field::itRAW);
+ *key_ptr++= (uchar) test(part->field->is_null());
+ part->field->get_key_image(key_ptr, part->length, Field::itRAW);
}
if (is_field_part)
{
@@ -700,31 +754,21 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
}
-/*
+/**
Check whether we can get value for {max|min}(field) by using a key.
- SYNOPSIS
- find_key_for_maxmin()
- max_fl in: 0 for MIN(field) / 1 for MAX(field)
- ref in/out Reference to the structure we store the key value
- field in: Field used inside MIN() / MAX()
- cond in: WHERE condition
- range_fl out: Bit flags for how to search if key is ok
- prefix_len out: Length of prefix for the search range
-
- DESCRIPTION
- If where condition is not a conjunction of 0 or more conjuct the
- function returns false, otherwise it checks whether there is an
- index including field as its k-th component/part such that:
-
- 1. for each previous component f_i there is one and only one conjunct
+ If where-condition is not a conjunction of 0 or more conjuct the
+ function returns false, otherwise it checks whether there is an
+ index including field as its k-th component/part such that:
+
+ -# for each previous component f_i there is one and only one conjunct
of the form: f_i= const_i or const_i= f_i or f_i is null
- 2. references to field occur only in conjucts of the form:
+ -# references to field occur only in conjucts of the form:
field {<|<=|>=|>|=} const or const {<|<=|>=|>|=} field or
field BETWEEN const1 AND const2
- 3. all references to the columns from the same table as column field
+ -# all references to the columns from the same table as column field
occur only in conjucts mentioned above.
- 4. each of k first components the index is not partial, i.e. is not
+ -# each of k first components the index is not partial, i.e. is not
defined on a fixed length proper prefix of the field.
If such an index exists the function through the ref parameter
@@ -732,17 +776,26 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
the length of first (k-1) components of the key and flags saying
how to apply the key for the search max/min value.
(if we have a condition field = const, prefix_len contains the length
- of the whole search key)
+ of the whole search key)
+
+ @param[in] max_fl 0 for MIN(field) / 1 for MAX(field)
+ @param[in,out] ref Reference to the structure we store the key value
+ @param[in] field Field used inside MIN() / MAX()
+ @param[in] cond WHERE condition
+ @param[out] range_fl Bit flags for how to search if key is ok
+ @param[out] prefix_len Length of prefix for the search range
- NOTE
+ @note
This function may set table->key_read to 1, which must be reset after
index is used! (This can only happen when function returns 1)
- RETURN
+ @retval
0 Index can not be used to optimize MIN(field)/MAX(field)
- 1 Can use key to optimize MIN()/MAX()
- In this case ref, range_fl and prefix_len are updated
-*/
+ @retval
+ 1 Can use key to optimize MIN()/MAX().
+ In this case ref, range_fl and prefix_len are updated
+*/
+
static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
Field* field, COND *cond,
@@ -786,6 +839,7 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
{
ref->key= idx;
ref->key_length= 0;
+ ref->key_parts= 0;
key_part_map key_part_used= 0;
*range_fl= NO_MIN_RANGE | NO_MAX_RANGE;
if (matching_cond(max_fl, ref, keyinfo, part, cond,
@@ -811,6 +865,8 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
/* Set the first byte of key_part_k to 1, that means NULL */
ref->key_buff[ref->key_length]= 1;
ref->key_length+= part->store_length;
+ ref->key_parts++;
+ DBUG_ASSERT(ref->key_parts == jdx+1);
*range_fl&= ~NO_MIN_RANGE;
*range_fl|= NEAR_MIN; // Open interval
}
@@ -832,20 +888,19 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
}
-/*
- Check whether found key is in range specified by conditions
+/**
+ Check whether found key is in range specified by conditions.
- SYNOPSIS
- reckey_in_range()
- max_fl in: 0 for MIN(field) / 1 for MAX(field)
- ref in: Reference to the key value and info
- field in: Field used the MIN/MAX expression
- cond in: WHERE condition
- range_fl in: Says whether there is a condition to to be checked
- prefix_len in: Length of the constant part of the key
+ @param[in] max_fl 0 for MIN(field) / 1 for MAX(field)
+ @param[in] ref Reference to the key value and info
+ @param[in] field Field used the MIN/MAX expression
+ @param[in] cond WHERE condition
+ @param[in] range_fl Says whether there is a condition to to be checked
+ @param[in] prefix_len Length of the constant part of the key
- RETURN
+ @retval
0 ok
+ @retval
1 WHERE was not true for the found row
*/
@@ -860,16 +915,16 @@ static int reckey_in_range(bool max_fl, TABLE_REF *ref, Field* field,
}
-/*
- Check whether {MAX|MIN}(field) is in range specified by conditions
- SYNOPSIS
- maxmin_in_range()
- max_fl in: 0 for MIN(field) / 1 for MAX(field)
- field in: Field used the MIN/MAX expression
- cond in: WHERE condition
+/**
+ Check whether {MAX|MIN}(field) is in range specified by conditions.
- RETURN
+ @param[in] max_fl 0 for MIN(field) / 1 for MAX(field)
+ @param[in] field Field used the MIN/MAX expression
+ @param[in] cond WHERE condition
+
+ @retval
0 ok
+ @retval
1 WHERE was not true for the found row
*/