home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 January
/
Chip_2001-01_cd1.bin
/
tema
/
mysql
/
mysql-3.23.28g-win-source.exe
/
sql
/
opt_sum.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2000-08-31
|
10KB
|
360 lines
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Optimizing of many different type of queries with GROUP functions */
#include "mysql_priv.h"
#include "sql_select.h"
static bool find_range_key(TABLE_REF *ref, Field* field,COND *cond);
/*****************************************************************************
** This function is only called for queries with sum functions and no
** GROUP BY part.
** This substitutes constants for some COUNT(), MIN() and MAX() functions.
** The function returns 1 if all items was resolved and -1 on impossible
** conditions
****************************************************************************/
int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
{
List_iterator<Item> it(all_fields);
int const_result=1;
bool recalc_const_item=0;
table_map removed_tables=0;
Item *item;
while ((item= it++))
{
if (item->type() == Item::SUM_FUNC_ITEM)
{
Item_sum *item_sum= (((Item_sum*) item));
switch (item_sum->sum_func()) {
case Item_sum::COUNT_FUNC:
/*
If the expr in count(expr) can never be null we can change this
to the number of rows in the tables
*/
if (!conds && !((Item_sum_count*) item)->args[0]->maybe_null)
{
longlong count=1;
TABLE_LIST *table;
for (table=tables; table ; table=table->next)
{
if (table->on_expr || (table->table->file->option_flag() &
HA_NOT_EXACT_COUNT))
{
const_result=0; // Can't optimize left join
break;
}
tables->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
count*= table->table->file->records;
}
if (!table)
{
((Item_sum_count*) item)->make_const(count);
recalc_const_item=1;
}
}
else
const_result=0;
break;
case Item_sum::MIN_FUNC:
{
/*
If MIN(expr) is the first part of a key or if all previous
parts of the key is found in the COND, then we can use
indexes to find the key.
*/
Item *expr=item_sum->args[0];
if (expr->type() == Item::FIELD_ITEM)
{
byte key_buff[MAX_KEY_LENGTH];
TABLE_REF ref;
ref.key_buff=key_buff;
if (!find_range_key(&ref, ((Item_field*) expr)->field,conds))
{
const_result=0;
break;
}
TABLE *table=((Item_field*) expr)->field->table;
bool error=table->file->index_init((uint) ref.key);
if (!ref.key_length)
error=table->file->index_first(table->record[0]) !=0;
else
error=table->file->index_read(table->record[0],key_buff,
ref.key_length,
HA_READ_KEY_OR_NEXT) ||
key_cmp(table, key_buff, ref.key, ref.key_length);
if (table->key_read)
{
table->key_read=0;
table->file->extra(HA_EXTRA_NO_KEYREAD);
}
table->file->index_end();
if (error)
return -1; // Impossible query
removed_tables|= table->map;
}
else if (!expr->const_item()) // This is VERY seldom false
{
const_result=0;
break;
}
((Item_sum_min*) item_sum)->reset();
((Item_sum_min*) item_sum)->make_const();
recalc_const_item=1;
break;
}
case Item_sum::MAX_FUNC:
{
/*
If MAX(expr) is the first part of a key or if all previous
parts of the key is found in the COND, then we can use
indexes to find the key.
*/
Item *expr=item_sum->args[0];
if (expr->type() == Item::FIELD_ITEM)
{
byte key_buff[MAX_KEY_LENGTH];
TABLE_REF ref;
ref.key_buff=key_buff;
if (!find_range_key(&ref, ((Item_field*) expr)->field,conds))
{
const_result=0;
break;
}
TABLE *table=((Item_field*) expr)->field->table;
bool error=table->file->index_init((uint) ref.key);
if (!ref.key_length)
error=table->file->index_last(table->record[0]) !=0;
else
{
(void) table->file->index_read(table->record[0], key_buff,
ref.key_length,
HA_READ_AFTER_KEY);
error=table->file->index_prev(table->record[0]) ||
key_cmp(table,key_buff,ref.key,ref.key_length);
}
if (table->key_read)
{
table->key_read=0;
table->file->extra(HA_EXTRA_NO_KEYREAD);
}
table->file->index_end();
if (error)
return -1; // Impossible query
removed_tables|= table->map;
}
else if (!expr->const_item()) // This is VERY seldom false
{
const_result=0;
break;
}
((Item_sum_min*) item_sum)->reset();
((Item_sum_min*) item_sum)->make_const();
recalc_const_item=1;
break;
}
default:
const_result=0;
break;
}
}
else if (const_result)
{
if (recalc_const_item)
item->update_used_tables();
if (!item->const_item())
const_result=0;
}
}
if (conds && (conds->used_tables() & ~ removed_tables))
const_result=0;
return const_result;
}
/* Count in how many times table is used (up to MAX_KEY_PARTS+1) */
uint count_table_entries(COND *cond,TABLE *table)
{
if (cond->type() == Item::COND_ITEM)
{
if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC)
return (cond->used_tables() & table->map) ? MAX_REF_PARTS+1 : 0;
List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
Item *item;
uint count=0;
while ((item=li++))
{
if ((count+=count_table_entries(item,table)) > MAX_REF_PARTS)
return MAX_REF_PARTS+1;
}
return count;
}
if (cond->type() == Item::FUNC_ITEM &&
(((Item_func*) cond)->functype() == Item_func::EQ_FUNC ||
(((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)) &&
cond->used_tables() == table->map)
{
Item *left_item= ((Item_func*) cond)->arguments()[0];
Item *right_item= ((Item_func*) cond)->arguments()[1];
if (left_item->type() == Item::FIELD_ITEM)
{
if (!(((Item_field*) left_item)->field->flags & PART_KEY_FLAG) ||
!right_item->const_item())
return MAX_REF_PARTS+1;
return 1;
}
if (right_item->type() == Item::FIELD_ITEM)
{
if (!(((Item_field*) right_item)->field->flags & PART_KEY_FLAG) ||
!left_item->const_item())
return MAX_REF_PARTS+1;
return 1;
}
}
return (cond->used_tables() & table->map) ? MAX_REF_PARTS+1 : 0;
}
/* check that the field is usable as key part */
bool part_of_cond(COND *cond,Field *field)
{
if (cond->type() == Item::COND_ITEM)
{
if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC)
return 0; // Already checked
List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
Item *item;
while ((item=li++))
{
if (part_of_cond(item,field))
return 1;
}
return 0;
}
if (cond->type() == Item::FUNC_ITEM &&
(((Item_func*) cond)->functype() == Item_func::EQ_FUNC ||
((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC) &&
cond->used_tables() == field->table->map)
{
Item *left_item= ((Item_func*) cond)->arguments()[0];
Item *right_item= ((Item_func*) cond)->arguments()[1];
if (left_item->type() == Item::FIELD_ITEM)
{
if (((Item_field*) left_item)->field != field ||
!right_item->const_item())
return 0;
}
else if (right_item->type() == Item::FIELD_ITEM)
{
if (((Item_field*) right_item)->field != field ||
!left_item->const_item())
return 0;
right_item=left_item; // const item in right
}
store_val_in_field(field,right_item);
return 1;
}
return 0;
}
/* Check if we can get value for field by using a key */
static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond)
{
if (!(field->flags & PART_KEY_FLAG))
return 0; // Not part of a key. Skipp it
TABLE *table=field->table;
if (table->file->option_flag() & HA_WRONG_ASCII_ORDER)
return(0); // Can't use key to find last row
uint idx=0;
/* Check if some key has field as first key part */
if (field->key_start && (! cond || ! (cond->used_tables() & table->map)))
{
for (key_map key=field->key_start ; !(key & 1) ; idx++)
key>>=1;
ref->key_length=0;
ref->key=idx;
if (field->part_of_key & ((table_map) 1 << idx))
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
}
return 1; // Ok to use key
}
/*
** Check if WHERE consist of exactly the previous key parts for some key
*/
if (!cond)
return 0;
uint table_entries= count_table_entries(cond,table);
if (!table_entries || table_entries > MAX_REF_PARTS)
return 0;
KEY *keyinfo,*keyinfo_end;
for (keyinfo=table->key_info, keyinfo_end=keyinfo+table->keys ;
keyinfo != keyinfo_end;
keyinfo++,idx++)
{
if (table_entries < keyinfo->key_parts)
{
byte *key_ptr=ref->key_buff;
KEY_PART_INFO *part,*part_end;
int left_length=MAX_KEY_LENGTH;
for (part=keyinfo->key_part, part_end=part+table_entries ;
part != part_end ;
part++)
{
if (!part_of_cond(cond,part->field))
break;
// Save found constant
if (part->null_bit)
*key_ptr++= (byte) test(part->field->is_null());
if (left_length - part->length < 0)
break; // Can't use this key
part->field->get_image((char*) key_ptr,part->length);
key_ptr+=part->length;
left_length-=part->length;
}
if (part == part_end && part->field == field)
{
ref->key_length= (uint) (key_ptr-ref->key_buff);
ref->key=idx;
if (field->part_of_key & ((table_map) 1 << idx))
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
}
return 1; // Ok to use key
}
}
}
return 0; // No possible key
}