summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <monty@mashka.mysql.fi>2003-06-26 05:38:19 +0300
committerunknown <monty@mashka.mysql.fi>2003-06-26 05:38:19 +0300
commita3beaaa3af7af26aabadda6836618d32d6c90e51 (patch)
treeaf26f54d96f10ded8e53e3634bc9beecf75bd15e /sql
parent3d5f6a8867c848459191ba320b573bd832e51d5a (diff)
downloadmariadb-git-a3beaaa3af7af26aabadda6836618d32d6c90e51.tar.gz
LEFT JOIN optimization: Change LEFT JOIN to normal join if possible
mysql-test/r/select.result: Added test for LEFT JOIN optimization mysql-test/t/select.test: Added test for LEFT JOIN optimization sql/item.h: LEFT JOIN optimization sql/item_cmpfunc.cc: LEFT JOIN optimization sql/item_cmpfunc.h: LEFT JOIN optimization sql/item_func.cc: LEFT JOIN optimization sql/item_func.h: LEFT JOIN optimization sql/item_strfunc.cc: LEFT JOIN optimization sql/sql_base.cc: Heart of LEFT JOIN optimization
Diffstat (limited to 'sql')
-rw-r--r--sql/item.h17
-rw-r--r--sql/item_cmpfunc.cc41
-rw-r--r--sql/item_cmpfunc.h18
-rw-r--r--sql/item_func.cc14
-rw-r--r--sql/item_func.h9
-rw-r--r--sql/item_strfunc.cc17
-rw-r--r--sql/sql_base.cc16
7 files changed, 101 insertions, 31 deletions
diff --git a/sql/item.h b/sql/item.h
index 09d428509d0..1631bf76135 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -71,7 +71,24 @@ public:
virtual double val_result() { return val(); }
virtual longlong val_int_result() { return val_int(); }
virtual String *str_result(String* tmp) { return val_str(tmp); }
+ /* bit map of tables used by item */
virtual table_map used_tables() const { return (table_map) 0L; }
+ /*
+ Return table map of tables that can't be NULL tables (tables that are
+ used in a context where if they would contain a NULL row generated
+ by a LEFT or RIGHT join, the item would not be true).
+ This expression is used on WHERE item to determinate if a LEFT JOIN can be
+ converted to a normal join.
+ Generally this function should return used_tables() if the function
+ would return null if any of the arguments are null
+ As this is only used in the beginning of optimization, the value don't
+ have to be updated in update_used_tables()
+ */
+ virtual table_map not_null_tables() const { return used_tables(); }
+ /*
+ Returns true if this is a simple constant item like an integer, not
+ a constant expression
+ */
virtual bool basic_const_item() const { return 0; }
virtual Item *new_item() { return 0; } /* Only for const items */
virtual cond_result eq_cmp_result() const { return COND_OK; }
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 3344f2bc01d..770fe3c0232 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -292,10 +292,12 @@ void Item_func_interval::fix_length_and_dec()
}
}
maybe_null=0; max_length=2;
- used_tables_cache|=item->used_tables();
+ used_tables_cache|= item->used_tables();
+ not_null_tables_cache&= item->not_null_tables();
with_sum_func= with_sum_func || item->with_sum_func;
}
+
void Item_func_interval::split_sum_func(List<Item> &fields)
{
if (item->with_sum_func && item->type() != SUM_FUNC_ITEM)
@@ -1073,8 +1075,9 @@ void Item_func_in::fix_length_and_dec()
}
maybe_null= item->maybe_null;
max_length=2;
- used_tables_cache|=item->used_tables();
- const_item_cache&=item->const_item();
+ used_tables_cache|= item->used_tables();
+ not_null_tables_cache&= item->not_null_tables();
+ const_item_cache&= item->const_item();
}
@@ -1174,14 +1177,21 @@ Item_cond::fix_fields(THD *thd,TABLE_LIST *tables)
char buff[sizeof(char*)]; // Max local vars in function
used_tables_cache=0;
const_item_cache=0;
+ /*
+ and_table_cache is the value that Item_cond_or() returns for
+ not_null_tables()
+ */
+ and_tables_cache= ~(table_map) 0;
if (thd && check_stack_overrun(thd,buff))
return 0; // Fatal error flag is set!
while ((item=li++))
{
+ table_map tmp_table_map;
while (item->type() == Item::COND_ITEM &&
((Item_cond*) item)->functype() == functype())
{ // Identical function
+
li.replace(((Item_cond*) item)->list);
((Item_cond*) item)->list.empty();
#ifdef DELETE_ITEMS
@@ -1193,9 +1203,12 @@ Item_cond::fix_fields(THD *thd,TABLE_LIST *tables)
item->top_level_item();
if (item->fix_fields(thd,tables))
return 1; /* purecov: inspected */
- used_tables_cache|=item->used_tables();
- with_sum_func= with_sum_func || item->with_sum_func;
- const_item_cache&=item->const_item();
+ used_tables_cache|= item->used_tables();
+ tmp_table_map= item->not_null_tables();
+ not_null_tables_cache|= tmp_table_map;
+ and_tables_cache&= tmp_table_map;
+ const_item_cache&= item->const_item();
+ with_sum_func= with_sum_func || item->with_sum_func;
if (item->maybe_null)
maybe_null=1;
}
@@ -1234,17 +1247,19 @@ Item_cond::used_tables() const
return used_tables_cache;
}
+
void Item_cond::update_used_tables()
{
- used_tables_cache=0;
- const_item_cache=1;
List_iterator_fast<Item> li(list);
Item *item;
+
+ used_tables_cache=0;
+ const_item_cache=1;
while ((item=li++))
{
item->update_used_tables();
- used_tables_cache|=item->used_tables();
- const_item_cache&= item->const_item();
+ used_tables_cache|= item->used_tables();
+ const_item_cache&= item->const_item();
}
}
@@ -1348,12 +1363,16 @@ Item *and_expressions(Item *a, Item *b, Item **org_item)
{
Item_cond *res;
if ((res= new Item_cond_and(a, (Item*) b)))
+ {
res->used_tables_cache= a->used_tables() | b->used_tables();
+ res->not_null_tables_cache= a->not_null_tables() | b->not_null_tables();
+ }
return res;
}
if (((Item_cond_and*) a)->add((Item*) b))
return 0;
((Item_cond_and*) a)->used_tables_cache|= b->used_tables();
+ ((Item_cond_and*) a)->not_null_tables_cache|= b->not_null_tables();
return a;
}
@@ -1489,6 +1508,8 @@ Item_func_regex::fix_fields(THD *thd,TABLE_LIST *tables)
max_length=1; decimals=0;
binary=args[0]->binary || args[1]->binary;
used_tables_cache=args[0]->used_tables() | args[1]->used_tables();
+ not_null_tables_cache= (args[0]->not_null_tables() |
+ args[1]->not_null_tables());
const_item_cache=args[0]->const_item() && args[1]->const_item();
if (!regex_compiled && args[1]->const_item())
{
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index f7ade97940c..536ac9dc3d4 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -204,7 +204,7 @@ public:
enum Item_result result_type () const { return cached_result_type; }
void fix_length_and_dec();
const char *func_name() const { return "ifnull"; }
- unsigned int size_of() { return sizeof(*this);}
+ table_map not_null_tables() const { return 0; }
};
@@ -224,7 +224,7 @@ public:
}
void fix_length_and_dec();
const char *func_name() const { return "if"; }
- unsigned int size_of() { return sizeof(*this);}
+ table_map not_null_tables() const { return 0; }
};
@@ -239,7 +239,7 @@ public:
enum Item_result result_type () const { return cached_result_type; }
void fix_length_and_dec();
const char *func_name() const { return "nullif"; }
- unsigned int size_of() { return sizeof(*this);}
+ table_map not_null_tables() const { return 0; }
};
@@ -254,9 +254,10 @@ public:
void fix_length_and_dec();
enum Item_result result_type () const { return cached_result_type; }
const char *func_name() const { return "coalesce"; }
- unsigned int size_of() { return sizeof(*this);}
+ table_map not_null_tables() const { return 0; }
};
+
class Item_func_case :public Item_func
{
Item * first_expr, *else_expr;
@@ -270,6 +271,7 @@ public:
String *val_str(String *);
void fix_length_and_dec();
void update_used_tables();
+ table_map not_null_tables() const { return 0; }
enum Item_result result_type () const { return cached_result_type; }
const char *func_name() const { return "case"; }
void print(String *str);
@@ -479,10 +481,12 @@ public:
}
}
}
+ table_map not_null_tables() const { return 0; }
optimize_type select_optimize() const { return OPTIMIZE_NULL; }
unsigned int size_of() { return sizeof(*this);}
};
+
class Item_func_isnotnull :public Item_bool_func
{
public:
@@ -495,9 +499,10 @@ public:
}
const char *func_name() const { return "isnotnull"; }
optimize_type select_optimize() const { return OPTIMIZE_NULL; }
- unsigned int size_of() { return sizeof(*this);}
+ table_map not_null_tables() const { return 0; }
};
+
class Item_func_like :public Item_bool_func2
{
char escape;
@@ -572,6 +577,8 @@ class Item_cond :public Item_bool_func
protected:
List<Item> list;
bool abort_on_null;
+ table_map and_tables_cache;
+
public:
/* Item_cond() is only used to create top level items */
Item_cond() : Item_bool_func(), abort_on_null(1) { const_item_cache=0; }
@@ -611,6 +618,7 @@ public:
enum Functype functype() const { return COND_OR_FUNC; }
longlong val_int();
const char *func_name() const { return "or"; }
+ table_map not_null_tables() const { return and_tables_cache; }
};
diff --git a/sql/item_func.cc b/sql/item_func.cc
index e847b203006..23bec0c3c81 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -61,7 +61,7 @@ Item_func::fix_fields(THD *thd,TABLE_LIST *tables)
Item **arg,**arg_end;
char buff[STACK_BUFF_ALLOC]; // Max argument in function
binary=0;
- used_tables_cache=0;
+ used_tables_cache= not_null_tables_cache= 0;
const_item_cache=1;
if (thd && check_stack_overrun(thd,buff))
@@ -78,8 +78,9 @@ Item_func::fix_fields(THD *thd,TABLE_LIST *tables)
if (item->binary)
binary=1;
with_sum_func= with_sum_func || item->with_sum_func;
- used_tables_cache|=item->used_tables();
- const_item_cache&= item->const_item();
+ used_tables_cache|= item->used_tables();
+ not_null_tables_cache|= item->not_null_tables();
+ const_item_cache&= item->const_item();
}
}
fix_length_and_dec();
@@ -122,6 +123,13 @@ table_map Item_func::used_tables() const
return used_tables_cache;
}
+
+table_map Item_func::not_null_tables() const
+{
+ return not_null_tables_cache;
+}
+
+
void Item_func::print(String *str)
{
str->append(func_name());
diff --git a/sql/item_func.h b/sql/item_func.h
index 68e5335dc7e..8a4cace0b87 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -34,7 +34,7 @@ protected:
Item **args,*tmp_arg[2];
public:
uint arg_count;
- table_map used_tables_cache;
+ table_map used_tables_cache, not_null_tables_cache;
bool const_item_cache;
enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
GE_FUNC,GT_FUNC,FT_FUNC,
@@ -97,6 +97,7 @@ public:
bool fix_fields(THD *,struct st_table_list *);
void make_field(Send_field *field);
table_map used_tables() const;
+ table_map not_null_tables() const;
void update_used_tables();
bool eq(const Item *item, bool binary_cmp) const;
virtual optimize_type select_optimize() const { return OPTIMIZE_NONE; }
@@ -588,7 +589,8 @@ public:
void split_sum_func(List<Item> &fields);
void update_used_tables()
{
- item->update_used_tables() ; Item_func::update_used_tables();
+ item->update_used_tables();
+ Item_func::update_used_tables();
used_tables_cache|= item->used_tables();
const_item_cache&= item->const_item();
}
@@ -597,6 +599,7 @@ public:
{
maybe_null=0; max_length=3;
used_tables_cache|= item->used_tables();
+ not_null_tables_cache&= item->not_null_tables();
const_item_cache&= item->const_item();
with_sum_func= with_sum_func || item->with_sum_func;
}
@@ -736,6 +739,7 @@ public:
return res;
}
Item_result result_type () const { return udf.result_type(); }
+ table_map not_null_tables() const { return 0; }
unsigned int size_of() { return sizeof(*this);}
};
@@ -969,6 +973,7 @@ public:
}
enum Functype functype() const { return FT_FUNC; }
void update_used_tables() {}
+ table_map not_null_tables() const { return 0; }
bool fix_fields(THD *thd,struct st_table_list *tlist);
bool eq(const Item *, bool binary_cmp) const;
longlong val_int() { return val()!=0.0; }
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 208be1ecd7f..20496a47671 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -606,9 +606,10 @@ void Item_func_concat_ws::fix_length_and_dec()
max_length=MAX_BLOB_WIDTH;
maybe_null=1;
}
- used_tables_cache|=separator->used_tables();
- const_item_cache&=separator->const_item();
- with_sum_func= with_sum_func || separator->with_sum_func;
+ used_tables_cache|= separator->used_tables();
+ not_null_tables_cache&= separator->not_null_tables();
+ const_item_cache&= separator->const_item();
+ with_sum_func= with_sum_func || separator->with_sum_func;
}
void Item_func_concat_ws::update_used_tables()
@@ -1509,8 +1510,9 @@ void Item_func_elt::fix_length_and_dec()
}
maybe_null=1; // NULL if wrong first arg
with_sum_func= with_sum_func || item->with_sum_func;
- used_tables_cache|=item->used_tables();
- const_item_cache&=item->const_item();
+ used_tables_cache|= item->used_tables();
+ not_null_tables_cache&= item->not_null_tables();
+ const_item_cache&= item->const_item();
}
@@ -1591,8 +1593,9 @@ void Item_func_make_set::fix_length_and_dec()
max_length=arg_count-1;
for (uint i=1 ; i < arg_count ; i++)
max_length+=args[i]->max_length;
- used_tables_cache|=item->used_tables();
- const_item_cache&=item->const_item();
+ used_tables_cache|= item->used_tables();
+ not_null_tables_cache&= item->not_null_tables();
+ const_item_cache&= item->const_item();
with_sum_func= with_sum_func || item->with_sum_func;
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 3bfd5e14d43..43718e5d93b 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1903,11 +1903,11 @@ bool setup_tables(TABLE_LIST *tables)
table->used_fields=0;
table->const_table=0;
- table->outer_join=table->null_row=0;
+ table->null_row=0;
table->status=STATUS_NO_RECORD;
table->keys_in_use_for_query= table->keys_in_use;
table->used_keys= table->keys_for_keyread;
- table->maybe_null=test(table->outer_join=table_list->outer_join);
+ table->maybe_null=test(table->outer_join= table_list->outer_join);
table->tablenr=tablenr;
table->map= (table_map) 1 << tablenr;
table->force_index= table_list->force_index;
@@ -2027,6 +2027,7 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
{
+ table_map not_null_tables= 0;
DBUG_ENTER("setup_conds");
thd->set_query_id=1;
thd->cond_count=0;
@@ -2036,6 +2037,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
thd->where="where clause";
if ((*conds)->fix_fields(thd,tables))
DBUG_RETURN(1);
+ not_null_tables= (*conds)->not_null_tables();
}
/* Check if we are using outer joins */
@@ -2049,9 +2051,15 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
DBUG_RETURN(1);
thd->cond_count++;
- /* If it's a normal join, add the ON/USING expression to the WHERE */
- if (!table->outer_join)
+ /*
+ If it's a normal join or a LEFT JOIN which can be optimized away
+ add the ON/USING expression to the WHERE
+ */
+ if (!table->outer_join ||
+ ((table->table->map & not_null_tables) &&
+ !(specialflag & SPECIAL_NO_NEW_FUNC)))
{
+ table->outer_join= 0;
if (!(*conds=and_conds(*conds, table->on_expr)))
DBUG_RETURN(1);
table->on_expr=0;