1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

Merge mysql.com:/home/psergey/mysql-5.0-bug8804-r12

into  mysql.com:/home/psergey/mysql-5.1-bug8804-r12-merge


sql/item_cmpfunc.cc:
  Auto merged
sql/item_cmpfunc.h:
  Auto merged
sql/item_subselect.h:
  Auto merged
sql/mysql_priv.h:
  Auto merged
sql/mysqld.cc:
  Auto merged
sql/sql_lex.h:
  Auto merged
sql/sql_select.cc:
  Auto merged
sql/sql_select.h:
  Auto merged
mysql-test/r/ndb_subquery.result:
  Manual merge
mysql-test/r/subselect.result:
  Manual merge
mysql-test/r/subselect3.result:
  Manual merge
sql/item_subselect.cc:
  Manual merge
This commit is contained in:
unknown
2007-01-24 22:28:28 +03:00
14 changed files with 1309 additions and 273 deletions

View File

@@ -234,16 +234,16 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
}
bool Item_subselect::exec(bool full_scan)
bool Item_subselect::exec()
{
int res;
res= engine->exec(full_scan);
res= engine->exec();
if (engine_changed)
{
engine_changed= 0;
return exec(full_scan);
return exec();
}
return (res);
}
@@ -491,13 +491,13 @@ bool Item_singlerow_subselect::null_inside()
void Item_singlerow_subselect::bring_value()
{
exec(FALSE);
exec();
}
double Item_singlerow_subselect::val_real()
{
DBUG_ASSERT(fixed == 1);
if (!exec(FALSE) && !value->null_value)
if (!exec() && !value->null_value)
{
null_value= 0;
return value->val_real();
@@ -512,7 +512,7 @@ double Item_singlerow_subselect::val_real()
longlong Item_singlerow_subselect::val_int()
{
DBUG_ASSERT(fixed == 1);
if (!exec(FALSE) && !value->null_value)
if (!exec() && !value->null_value)
{
null_value= 0;
return value->val_int();
@@ -526,7 +526,7 @@ longlong Item_singlerow_subselect::val_int()
String *Item_singlerow_subselect::val_str(String *str)
{
if (!exec(FALSE) && !value->null_value)
if (!exec() && !value->null_value)
{
null_value= 0;
return value->val_str(str);
@@ -541,7 +541,7 @@ String *Item_singlerow_subselect::val_str(String *str)
my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
{
if (!exec(FALSE) && !value->null_value)
if (!exec() && !value->null_value)
{
null_value= 0;
return value->val_decimal(decimal_value);
@@ -556,7 +556,7 @@ my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value)
bool Item_singlerow_subselect::val_bool()
{
if (!exec(FALSE) && !value->null_value)
if (!exec() && !value->null_value)
{
null_value= 0;
return value->val_bool();
@@ -608,7 +608,7 @@ bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit)
Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex):
Item_exists_subselect(), optimizer(0), transformed(0),
enable_pushed_conds(TRUE), upper_item(0)
pushed_cond_guards(NULL), upper_item(0)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
left_expr= left_exp;
@@ -653,7 +653,7 @@ void Item_exists_subselect::fix_length_and_dec()
double Item_exists_subselect::val_real()
{
DBUG_ASSERT(fixed == 1);
if (exec(FALSE))
if (exec())
{
reset();
return 0;
@@ -664,7 +664,7 @@ double Item_exists_subselect::val_real()
longlong Item_exists_subselect::val_int()
{
DBUG_ASSERT(fixed == 1);
if (exec(FALSE))
if (exec())
{
reset();
return 0;
@@ -675,7 +675,7 @@ longlong Item_exists_subselect::val_int()
String *Item_exists_subselect::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
if (exec(FALSE))
if (exec())
{
reset();
return 0;
@@ -688,7 +688,7 @@ String *Item_exists_subselect::val_str(String *str)
my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
if (exec(FALSE))
if (exec())
{
reset();
return 0;
@@ -701,7 +701,7 @@ my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value)
bool Item_exists_subselect::val_bool()
{
DBUG_ASSERT(fixed == 1);
if (exec(FALSE))
if (exec())
{
reset();
return 0;
@@ -719,7 +719,7 @@ double Item_in_subselect::val_real()
DBUG_ASSERT(0);
DBUG_ASSERT(fixed == 1);
null_value= 0;
if (exec(!enable_pushed_conds))
if (exec())
{
reset();
null_value= 1;
@@ -740,7 +740,7 @@ longlong Item_in_subselect::val_int()
DBUG_ASSERT(0);
DBUG_ASSERT(fixed == 1);
null_value= 0;
if (exec(!enable_pushed_conds))
if (exec())
{
reset();
null_value= 1;
@@ -761,7 +761,7 @@ String *Item_in_subselect::val_str(String *str)
DBUG_ASSERT(0);
DBUG_ASSERT(fixed == 1);
null_value= 0;
if (exec(!enable_pushed_conds))
if (exec())
{
reset();
null_value= 1;
@@ -781,7 +781,7 @@ bool Item_in_subselect::val_bool()
{
DBUG_ASSERT(fixed == 1);
null_value= 0;
if (exec(!enable_pushed_conds))
if (exec())
{
reset();
null_value= 1;
@@ -801,7 +801,7 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value)
DBUG_ASSERT(0);
null_value= 0;
DBUG_ASSERT(fixed == 1);
if (exec(!enable_pushed_conds))
if (exec())
{
reset();
null_value= 1;
@@ -965,7 +965,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
if (!substitution)
{
//first call for this unit
/* We're invoked for the 1st (or the only) SELECT in the subquery UNION */
SELECT_LEX_UNIT *unit= select_lex->master_unit();
substitution= optimizer;
@@ -991,19 +991,14 @@ Item_in_subselect::single_value_transformer(JOIN *join,
unit->uncacheable|= UNCACHEABLE_DEPENDENT;
}
if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards)
{
if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool))))
DBUG_RETURN(RES_ERROR);
pushed_cond_guards[0]= TRUE;
}
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
/*
Add the left part of a subselect to a WHERE or HAVING clause of
the right part, e.g.
SELECT 1 IN (SELECT a FROM t1) =>
SELECT Item_in_optimizer(1, SELECT a FROM t1 WHERE a=1)
HAVING is used only if the right part contains a SUM function, a GROUP
BY or a HAVING clause.
*/
if (join->having || select_lex->with_sum_func ||
select_lex->group_list.elements)
{
@@ -1015,13 +1010,13 @@ Item_in_subselect::single_value_transformer(JOIN *join,
ref_pointer_array,
(char *)"<ref>",
this->full_name()));
if (!abort_on_null && ((Item*)select_lex->item_list.head())->maybe_null)
if (!abort_on_null && left_expr->maybe_null)
{
/*
We can encounter "NULL IN (SELECT ...)". Wrap the added condition
within a trigger.
within a trig_cond.
*/
item= new Item_func_trig_cond(item, &enable_pushed_conds);
item= new Item_func_trig_cond(item, get_cond_guard(0));
}
/*
@@ -1030,6 +1025,8 @@ Item_in_subselect::single_value_transformer(JOIN *join,
argument (reference) to fix_fields()
*/
select_lex->having= join->having= and_items(join->having, item);
if (join->having == item)
item->name= (char*)in_having_cond;
select_lex->having_fix_field= 1;
/*
we do not check join->having->fixed, because Item_and (from and_items)
@@ -1056,14 +1053,19 @@ Item_in_subselect::single_value_transformer(JOIN *join,
item= func->create(expr, item);
if (!abort_on_null && orig_item->maybe_null)
{
having=
new Item_func_trig_cond(new Item_is_not_null_test(this, having),
&enable_pushed_conds);
having= new Item_is_not_null_test(this, having);
if (left_expr->maybe_null)
{
if (!(having= new Item_func_trig_cond(having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
/*
Item_is_not_null_test can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last
argument (reference) to fix_fields()
*/
having->name= (char*)in_having_cond;
select_lex->having= join->having= having;
select_lex->having_fix_field= 1;
/*
@@ -1075,17 +1077,25 @@ Item_in_subselect::single_value_transformer(JOIN *join,
select_lex->having_fix_field= 0;
if (tmp)
DBUG_RETURN(RES_ERROR);
/*
NOTE: It is important that we add this "IS NULL" here, even when
orig_item can't be NULL. This is needed so that this predicate is
only used by ref[_or_null] analyzer (and, e.g. is not used by const
propagation).
*/
item= new Item_cond_or(item,
new Item_func_isnull(orig_item));
item= new Item_func_trig_cond(item, &enable_pushed_conds);
}
/*
If we may encounter NULL IN (SELECT ...) and care whether subquery
result is NULL or FALSE, wrap condition in a trig_cond.
*/
if (!abort_on_null && left_expr->maybe_null)
{
if (!(item= new Item_func_trig_cond(item, get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
/*
TODO: figure out why the following is done here in
single_value_transformer but there is no corresponding action in
row_value_transformer?
*/
item->name= (char *)in_additional_cond;
/*
AND can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last
@@ -1116,10 +1126,16 @@ Item_in_subselect::single_value_transformer(JOIN *join,
select_lex->ref_pointer_array,
(char *)"<no matter>",
(char *)"<result>"));
new_having= new Item_func_trig_cond(new_having, &enable_pushed_conds);
if (!abort_on_null && left_expr->maybe_null)
{
if (!(new_having= new Item_func_trig_cond(new_having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
new_having->name= (char*)in_having_cond;
select_lex->having= join->having= new_having;
select_lex->having_fix_field= 1;
/*
we do not check join->having->fixed, because comparison function
(from func->create) can't be fixed after creation
@@ -1189,6 +1205,15 @@ Item_in_subselect::row_value_transformer(JOIN *join)
thd->lex->current_select= current;
unit->uncacheable|= UNCACHEABLE_DEPENDENT;
if (!abort_on_null && left_expr->maybe_null && !pushed_cond_guards)
{
if (!(pushed_cond_guards= (bool*)join->thd->alloc(sizeof(bool) *
left_expr->cols())))
DBUG_RETURN(RES_ERROR);
for (uint i= 0; i < cols_num; i++)
pushed_cond_guards[i]= TRUE;
}
}
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
@@ -1205,6 +1230,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
is_not_null_test(v3))
where is_not_null_test used to register nulls in case if we have
not found matching to return correct NULL value
TODO: say here explicitly if the order of AND parts matters or not.
*/
Item *item_having_part2= 0;
for (uint i= 0; i < cols_num; i++)
@@ -1233,21 +1259,28 @@ Item_in_subselect::row_value_transformer(JOIN *join)
(char *)"<no matter>",
(char *)"<list ref>")
);
having_item=
and_items(having_item,
new Item_cond_or(item_eq, item_isnull));
item_having_part2=
and_items(item_having_part2,
new
Item_is_not_null_test(this,
new
Item_ref(&select_lex->context,
select_lex->
ref_pointer_array + i,
(char *)"<no matter>",
(char *)"<list ref>")
)
);
Item *col_item= new Item_cond_or(item_eq, item_isnull);
if (!abort_on_null && left_expr->el(i)->maybe_null)
{
if (!(col_item= new Item_func_trig_cond(col_item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
}
having_item= and_items(having_item, col_item);
Item *item_nnull_test=
new Item_is_not_null_test(this,
new Item_ref(&select_lex->context,
select_lex->
ref_pointer_array + i,
(char *)"<no matter>",
(char *)"<list ref>"));
if (!abort_on_null && left_expr->el(i)->maybe_null)
{
if (!(item_nnull_test=
new Item_func_trig_cond(item_nnull_test, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
}
item_having_part2= and_items(item_having_part2, item_nnull_test);
item_having_part2->top_level_item();
}
having_item= and_items(having_item, item_having_part2);
@@ -1296,18 +1329,15 @@ Item_in_subselect::row_value_transformer(JOIN *join)
);
if (!abort_on_null)
{
having_item=
and_items(having_item,
new
Item_is_not_null_test(this,
new
Item_ref(&select_lex->context,
select_lex->
ref_pointer_array + i,
(char *)"<no matter>",
(char *)"<list ref>")
)
);
Item *having_col_item=
new Item_is_not_null_test(this,
new
Item_ref(&select_lex->context,
select_lex->ref_pointer_array + i,
(char *)"<no matter>",
(char *)"<list ref>"));
item_isnull= new
Item_func_isnull(new
Item_direct_ref(&select_lex->context,
@@ -1316,14 +1346,23 @@ Item_in_subselect::row_value_transformer(JOIN *join)
(char *)"<no matter>",
(char *)"<list ref>")
);
item= new Item_cond_or(item, item_isnull);
/*
TODO: why we create the above for cases where the right part
cant be NULL?
*/
if (left_expr->el(i)->maybe_null)
{
if (!(item= new Item_func_trig_cond(item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
if (!(having_col_item=
new Item_func_trig_cond(having_col_item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
}
having_item= and_items(having_item, having_col_item);
}
where_item= and_items(where_item, item);
}
if (where_item)
where_item= new Item_func_trig_cond(where_item, &enable_pushed_conds);
/*
AND can't be changed during fix_fields()
we can assign select_lex->where here, and pass 0 as last
@@ -1337,9 +1376,9 @@ Item_in_subselect::row_value_transformer(JOIN *join)
if (having_item)
{
bool res;
having_item= new Item_func_trig_cond(having_item, &enable_pushed_conds);
select_lex->having= join->having= and_items(join->having, having_item);
if (having_item == select_lex->having)
having_item->name= (char*)in_having_cond;
select_lex->having->top_level_item();
/*
AND can't be changed during fix_fields()
@@ -1725,7 +1764,7 @@ int init_read_record_seq(JOIN_TAB *tab);
int join_read_always_key_or_null(JOIN_TAB *tab);
int join_read_next_same_or_null(READ_RECORD *info);
int subselect_single_select_engine::exec(bool full_scan)
int subselect_single_select_engine::exec()
{
DBUG_ENTER("subselect_single_select_engine::exec");
char const *save_where= thd->where;
@@ -1763,9 +1802,13 @@ int subselect_single_select_engine::exec(bool full_scan)
if (!executed)
{
item->reset_value_registration();
if (full_scan)
bool have_changed_access= FALSE;
JOIN_TAB *changed_tabs[MAX_TABLES];
JOIN_TAB **last_changed_tab= changed_tabs;
if (item->have_guarded_conds())
{
/*
For at least one of the pushed predicates the following is true:
We should not apply optimizations based on the condition that was
pushed down into the subquery. Those optimizations are ref[_or_null]
acceses. Change them to be full table scans.
@@ -1773,32 +1816,36 @@ int subselect_single_select_engine::exec(bool full_scan)
for (uint i=join->const_tables ; i < join->tables ; i++)
{
JOIN_TAB *tab=join->join_tab+i;
if (tab->keyuse && tab->keyuse->outer_ref)
if (tab && tab->keyuse)
{
tab->read_first_record= init_read_record_seq;
tab->read_record.record= tab->table->record[0];
tab->read_record.thd= join->thd;
tab->read_record.ref_length= tab->table->file->ref_length;
for (uint i= 0; i < tab->ref.key_parts; i++)
{
bool *cond_guard= tab->ref.cond_guards[i];
if (cond_guard && !*cond_guard)
{
/* Change the access method to full table scan */
tab->read_first_record= init_read_record_seq;
tab->read_record.record= tab->table->record[0];
tab->read_record.thd= join->thd;
tab->read_record.ref_length= tab->table->file->ref_length;
*(last_changed_tab++)= tab;
break;
}
}
}
}
}
join->exec();
if (full_scan)
/* Enable the optimizations back */
for (JOIN_TAB **ptab= changed_tabs; ptab != last_changed_tab; ptab++)
{
/* Enable the optimizations back */
for (uint i=join->const_tables ; i < join->tables ; i++)
{
JOIN_TAB *tab=join->join_tab+i;
if (tab->keyuse && tab->keyuse->outer_ref)
{
tab->read_record.record= 0;
tab->read_record.ref_length= 0;
tab->read_first_record= join_read_always_key_or_null;
tab->read_record.read_record= join_read_next_same_or_null;
}
}
JOIN_TAB *tab= *ptab;
tab->read_record.record= 0;
tab->read_record.ref_length= 0;
tab->read_first_record= join_read_always_key_or_null;
tab->read_record.read_record= join_read_next_same_or_null;
}
executed= 1;
thd->where= save_where;
@@ -1810,13 +1857,9 @@ int subselect_single_select_engine::exec(bool full_scan)
DBUG_RETURN(0);
}
int subselect_union_engine::exec(bool full_scan)
int subselect_union_engine::exec()
{
char const *save_where= thd->where;
/*
Ignore the full_scan parameter: the pushed down predicates are only used
for filtering, and the caller has disabled them if necessary.
*/
int res= unit->exec();
thd->where= save_where;
return res;
@@ -1824,7 +1867,7 @@ int subselect_union_engine::exec(bool full_scan)
/*
Search for at least on row satisfying select condition
Search for at least one row satisfying select condition
SYNOPSIS
subselect_uniquesubquery_engine::scan_table()
@@ -1833,8 +1876,8 @@ int subselect_union_engine::exec(bool full_scan)
Scan the table using sequential access until we find at least one row
satisfying select condition.
The result of this function (info about whether a row was found) is
stored in this->empty_result_set.
The caller must set this->empty_result_set=FALSE before calling this
function. This function will set it to TRUE if it finds a matching row.
RETURN
FALSE - OK
@@ -1846,7 +1889,6 @@ int subselect_uniquesubquery_engine::scan_table()
int error;
TABLE *table= tab->table;
DBUG_ENTER("subselect_uniquesubquery_engine::scan_table");
empty_result_set= TRUE;
if (table->file->inited)
table->file->ha_index_end();
@@ -1939,10 +1981,13 @@ bool subselect_uniquesubquery_engine::copy_ref_key()
- FALSE otherwise.
In some cases (IN subselect is a top level item, i.e. abort_on_null==TRUE)
the caller doesn't distinguish between NULL and FALSE result and we just
the caller doesn't distinguish between NULL and FALSE result and we just
return FALSE.
Otherwise we make a full table scan to see if there is at least one matching row.
Otherwise we make a full table scan to see if there is at least one
matching row.
The result of this function (info about whether a row was found) is
stored in this->empty_result_set.
NOTE
RETURN
@@ -1950,11 +1995,12 @@ bool subselect_uniquesubquery_engine::copy_ref_key()
TRUE - an error occured while scanning
*/
int subselect_uniquesubquery_engine::exec(bool full_scan)
int subselect_uniquesubquery_engine::exec()
{
DBUG_ENTER("subselect_uniquesubquery_engine::exec");
int error;
TABLE *table= tab->table;
empty_result_set= TRUE;
/* TODO: change to use of 'full_scan' here? */
if (copy_ref_key())
@@ -1975,9 +2021,13 @@ int subselect_uniquesubquery_engine::exec(bool full_scan)
{
error= 0;
table->null_row= 0;
((Item_in_subselect *) item)->value= (!table->status &&
(!cond || cond->val_int()) ? 1 :
0);
if (!table->status && (!cond || cond->val_int()))
{
((Item_in_subselect *) item)->value= 1;
empty_result_set= FALSE;
}
else
((Item_in_subselect *) item)->value= 0;
}
DBUG_RETURN(error != 0);
@@ -2043,7 +2093,7 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine()
1
*/
int subselect_indexsubquery_engine::exec(bool full_scan)
int subselect_indexsubquery_engine::exec()
{
DBUG_ENTER("subselect_indexsubquery_engine::exec");
int error;
@@ -2084,8 +2134,9 @@ int subselect_indexsubquery_engine::exec(bool full_scan)
table->null_row= 0;
if (!table->status)
{
if (!cond || cond->val_int())
if ((!cond || cond->val_int()) && (!having || having->val_int()))
{
empty_result_set= FALSE;
if (null_finding)
((Item_in_subselect *) item)->was_null= 1;
else
@@ -2228,11 +2279,16 @@ void subselect_indexsubquery_engine::print(String *str)
str->append(key_info->name);
if (check_null)
str->append(STRING_WITH_LEN(" checking NULL"));
if (cond)
if (cond)
{
str->append(STRING_WITH_LEN(" where "));
cond->print(str);
}
if (having)
{
str->append(STRING_WITH_LEN(" having "));
having->print(str);
}
str->append(')');
}