mariadb/sql/item_subselect.cc
unknown 76bf7d2224 Added missing SSL library (Should be in source distribution)
Fixed compiler warnings (a lot of hidden variables detected by the Forte compiler)
Added a lot of 'version_xxx' strings to 'show variables'
Prevent copying of TMP_TABLE_PARAM (This caused core dump bug on Solaris)
Fixed problem with printing sub selects to debug log


Docs/mysqld_error.txt:
  Updated error messages
Makefile.am:
  Added missing SSL library (Should be in source distribution)
configure.in:
  Added missing SSL library
include/sql_common.h:
  Move duplicated prototypes
innobase/os/os0file.c:
  Added comment for line that could be removed
innobase/srv/srv0srv.c:
  Added comment for line that could be removed
innobase/srv/srv0start.c:
  Added comment for line that could be removed
innobase/trx/trx0sys.c:
  Added cast to remove compiler warning
isam/isamchk.c:
  Fixed compiler warning
libmysql/conf_to_src.c:
  Include files in proper order
myisam/mi_check.c:
  Removed else part that caused compiler warning
myisam/mi_delete.c:
  Added cast
myisam/mi_page.c:
  Added cast
myisam/mi_preload.c:
  Added cast
myisam/mi_write.c:
  Added cast
myisam/myisampack.c:
  changed 'byte' to 'current_byte' to avoid compiler warnings
mysql-test/mysql-test-run.sh:
  Removed start-from as test '<' is not portable and this can easily be done from command line
mysys/hash.c:
  Added cast
mysys/mf_wcomp.c:
  Removed not reached line
mysys/my_append.c:
  Fixed include file order to get this more portable
mysys/my_copy.c:
  Fixed include file order to get this more portable
mysys/my_redel.c:
  Fixed include file order to get this more portable
sql-common/client.c:
  More DBUG_PRINT
sql-common/pack.c:
  Added casts becasue Fortre compiler apparently compares (ulonglong) < (longlong) as signed
sql/ha_heap.cc:
  Changed variable names to not cause hidden variables
sql/ha_innodb.cc:
  Changed variable names to not cause hidden variables
sql/item.cc:
  Changed variable names to not cause hidden variables
sql/item.h:
  Changed variable names to not cause hidden variables
sql/item_cmpfunc.h:
  Changed variable names to not cause hidden variables
sql/item_func.cc:
  Changed variable names to not cause hidden variables
sql/item_subselect.cc:
  Changed variable names to not cause hidden variables
sql/item_subselect.h:
  Changed variable names to not cause hidden variables
sql/item_sum.cc:
  Changed variable names to not cause hidden variables
sql/item_timefunc.cc:
  Changed variable names to not cause hidden variables
sql/log.cc:
  Changed variable names to not cause hidden variables
sql/protocol.cc:
  Changed variable names to not cause hidden variables
sql/protocol.h:
  Changed variable names to not cause hidden variables
  Remove function not declared in protocol.cc
sql/protocol_cursor.cc:
  Changed variable names to not cause hidden variables
sql/set_var.cc:
  Added a lot of 'version_xxx' strings
  Changed 'bdb_version' to 'version_bdb'
sql/sql_class.cc:
  Changed variable names to not cause hidden variables
  Add TMP_TABLE_PARAM::init() to allow one to initialize structure several times
sql/sql_class.h:
  Prevent copying of TMP_TABLE_PARAM (This caused core dump bug on Solaris)
sql/sql_derived.cc:
  Avoid copying TMP_TABLE_PARAM (Use class version instead)
sql/sql_error.cc:
  More DBUG
sql/sql_help.cc:
  Fixed compiler warning
sql/sql_lex.cc:
  Changed variable names to not cause hidden variables
sql/sql_list.h:
  Changed variable names to not cause hidden variables
sql/sql_parse.cc:
  Changed variable names to not cause hidden variables
sql/sql_select.cc:
  Changed variable names to not cause hidden variables
  Ensure that you don't send NULL to printf() for %s
  Fixed problem with printing sub selects to debug log
sql/sql_select.h:
  Changed variable names to not cause hidden variables
sql/sql_union.cc:
  Changed variable names to not cause hidden variables
  Don't use local copy of TMP_TABLE_PARAM (Caused core dump on Solaris)
sql/sql_update.cc:
  Indentation cleanup
sql/sql_yacc.yy:
  Remove warning
strings/conf_to_src.c:
  Fixed include file order
2003-11-28 12:18:13 +02:00

1276 lines
30 KiB
C++

/* Copyright (C) 2000 MySQL 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 */
/*
subselect Item
SUBSELECT TODO:
- add function from mysql_select that use JOIN* as parameter to JOIN methods
(sql_select.h/sql_select.cc)
*/
#ifdef __GNUC__
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
#include "sql_select.h"
inline Item * and_items(Item* cond, Item *item)
{
return (cond? (new Item_cond_and(cond, item)) : item);
}
Item_subselect::Item_subselect():
Item_result_field(), value_assigned(0), substitution(0),
engine(0), used_tables_cache(0), have_to_be_excluded(0),
const_item_cache(1), engine_changed(0)
{
reset();
/*
item value is NULL if select_subselect not changed this value
(i.e. some rows will be found returned)
*/
null_value= 1;
}
void Item_subselect::init(st_select_lex *select_lex,
select_subselect *result)
{
DBUG_ENTER("Item_subselect::init");
DBUG_PRINT("subs", ("select_lex 0x%xl", (ulong) select_lex));
if (select_lex->next_select())
engine= new subselect_union_engine(select_lex->master_unit(), result,
this);
else
engine= new subselect_single_select_engine(select_lex, result, this);
DBUG_VOID_RETURN;
}
Item_subselect::~Item_subselect()
{
delete engine;
}
Item_subselect::trans_res
Item_subselect::select_transformer(JOIN *join)
{
DBUG_ENTER("Item_subselect::select_transformer");
DBUG_RETURN(RES_OK);
}
bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref)
{
engine->set_thd((thd= thd_param));
char const *save_where= thd->where;
int res= engine->prepare();
if (!res)
{
if (substitution)
{
(*ref)= substitution;
substitution->name= name;
if (have_to_be_excluded)
engine->exclude();
substitution= 0;
fixed= 1;
thd->where= "checking transformed subquery";
int ret= (*ref)->fix_fields(thd, tables, ref);
// We can't substitute aggregate functions (like (SELECT (max(i)))
if ((*ref)->with_sum_func)
{
my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0));
return 1;
}
return ret;
}
// Is it one field subselect?
if (engine->cols() > max_columns)
{
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
return 1;
}
fix_length_and_dec();
}
uint8 uncacheable= engine->uncacheable();
if (uncacheable)
{
const_item_cache= 0;
if (uncacheable & UNCACHEABLE_RAND)
used_tables_cache|= RAND_TABLE_BIT;
}
fixed= 1;
thd->where= save_where;
return res;
}
bool Item_subselect::exec()
{
int res;
MEM_ROOT *old_root= my_pthread_getspecific_ptr(MEM_ROOT*, THR_MALLOC);
if (&thd->mem_root != old_root)
{
my_pthread_setspecific_ptr(THR_MALLOC, &thd->mem_root);
res= engine->exec();
my_pthread_setspecific_ptr(THR_MALLOC, old_root);
}
else
res= engine->exec();
if (engine_changed)
{
engine_changed= 0;
return exec();
}
return (res);
}
Item::Type Item_subselect::type() const
{
return SUBSELECT_ITEM;
}
void Item_subselect::fix_length_and_dec()
{
engine->fix_length_and_dec(0);
}
table_map Item_subselect::used_tables() const
{
return (table_map) (engine->uncacheable() ? used_tables_cache : 0L);
}
bool Item_subselect::const_item() const
{
return const_item_cache;
}
void Item_subselect::update_used_tables()
{
if (!engine->uncacheable())
{
// did all used tables become ststic?
if (!(used_tables_cache & ~engine->upper_select_const_tables()))
const_item_cache= 1;
}
}
void Item_subselect::print(String *str)
{
str->append('(');
engine->print(str);
str->append(')');
}
Item_singlerow_subselect::Item_singlerow_subselect(st_select_lex *select_lex)
:Item_subselect(), value(0)
{
DBUG_ENTER("Item_singlerow_subselect::Item_singlerow_subselect");
init(select_lex, new select_singlerow_subselect(this));
max_columns= 1;
maybe_null= 1;
max_columns= UINT_MAX;
DBUG_VOID_RETURN;
}
Item_maxmin_subselect::Item_maxmin_subselect(Item_subselect *parent,
st_select_lex *select_lex,
bool max_arg)
:Item_singlerow_subselect()
{
DBUG_ENTER("Item_maxmin_subselect::Item_maxmin_subselect");
max= max_arg;
init(select_lex, new select_max_min_finder_subselect(this, max_arg));
max_columns= 1;
maybe_null= 1;
max_columns= 1;
/*
Following information was collected during performing fix_fields()
of Items belonged to subquery, which will be not repeated
*/
used_tables_cache= parent->get_used_tables_cache();
const_item_cache= parent->get_const_item_cache();
DBUG_VOID_RETURN;
}
void Item_maxmin_subselect::print(String *str)
{
str->append(max?"<max>":"<min>", 5);
Item_singlerow_subselect::print(str);
}
void Item_singlerow_subselect::reset()
{
null_value= 1;
if (value)
value->null_value= 1;
}
Item_subselect::trans_res
Item_singlerow_subselect::select_transformer(JOIN *join)
{
SELECT_LEX *select_lex= join->select_lex;
if (!select_lex->master_unit()->first_select()->next_select() &&
!select_lex->table_list.elements &&
select_lex->item_list.elements == 1 &&
/*
We cant change name of Item_field or Item_ref, because it will
prevent it's correct resolving, but we should save name of
removed item => we do not make optimization if top item of
list is field or reference.
TODO: solve above problem
*/
!(select_lex->item_list.head()->type() == FIELD_ITEM ||
select_lex->item_list.head()->type() == REF_ITEM)
)
{
have_to_be_excluded= 1;
if (join->thd->lex.describe)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
push_warning(join->thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SELECT_REDUCED, warn_buff);
}
substitution= select_lex->item_list.head();
/*
as far as we moved content to upper leven, field which depend of
'upper' select is not really dependent => we remove this dependence
*/
substitution->walk(&Item::remove_dependence_processor,
(byte *) select_lex->outer_select());
if (join->conds || join->having)
{
Item *cond;
if (!join->having)
cond= join->conds;
else if (!join->conds)
cond= join->having;
else
if (!(cond= new Item_cond_and(join->conds, join->having)))
return RES_ERROR;
if (!(substitution= new Item_func_if(cond, substitution,
new Item_null())))
return RES_ERROR;
}
return RES_REDUCE;
}
return RES_OK;
}
void Item_singlerow_subselect::store(uint i, Item *item)
{
row[i]->store(item);
}
enum Item_result Item_singlerow_subselect::result_type() const
{
return engine->type();
}
void Item_singlerow_subselect::fix_length_and_dec()
{
if ((max_columns= engine->cols()) == 1)
{
engine->fix_length_and_dec(row= &value);
if (!(value= Item_cache::get_cache(engine->type())))
return;
}
else
{
if (!(row= (Item_cache**) sql_alloc(sizeof(Item_cache*)*max_columns)))
return;
engine->fix_length_and_dec(row);
value= *row;
}
maybe_null= engine->may_be_null();
}
uint Item_singlerow_subselect::cols()
{
return engine->cols();
}
bool Item_singlerow_subselect::check_cols(uint c)
{
if (c != engine->cols())
{
my_error(ER_OPERAND_COLUMNS, MYF(0), c);
return 1;
}
return 0;
}
bool Item_singlerow_subselect::null_inside()
{
for (uint i= 0; i < max_columns ; i++)
{
if (row[i]->null_value)
return 1;
}
return 0;
}
void Item_singlerow_subselect::bring_value()
{
exec();
}
double Item_singlerow_subselect::val ()
{
if (!exec() && !value->null_value)
{
null_value= 0;
return value->val();
}
else
{
reset();
return 0;
}
}
longlong Item_singlerow_subselect::val_int ()
{
if (!exec() && !value->null_value)
{
null_value= 0;
return value->val_int();
}
else
{
reset();
return 0;
}
}
String *Item_singlerow_subselect::val_str (String *str)
{
if (!exec() && !value->null_value)
{
null_value= 0;
return value->val_str(str);
}
else
{
reset();
return 0;
}
}
Item_exists_subselect::Item_exists_subselect(st_select_lex *select_lex):
Item_subselect()
{
DBUG_ENTER("Item_exists_subselect::Item_exists_subselect");
init(select_lex, new select_exists_subselect(this));
max_columns= UINT_MAX;
null_value= 0; //can't be NULL
maybe_null= 0; //can't be NULL
value= 0;
// We need only 1 row to determinate existence
select_lex->master_unit()->global_parameters->select_limit= 1;
DBUG_VOID_RETURN;
}
void Item_exists_subselect::print(String *str)
{
str->append("exists", 6);
Item_subselect::print(str);
}
bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit)
{
if (unit->fake_select_lex &&
unit->fake_select_lex->test_limit())
return(1);
SELECT_LEX *sl= unit->first_select();
for (; sl; sl= sl->next_select())
{
if (sl->test_limit())
return(1);
}
return(0);
}
Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex):
Item_exists_subselect(), transformed(0), upper_not(0)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
left_expr= left_exp;
init(select_lex, new select_exists_subselect(this));
max_columns= UINT_MAX;
maybe_null= 1;
abort_on_null= 0;
reset();
//if test_limit will fail then error will be reported to client
test_limit(select_lex->master_unit());
DBUG_VOID_RETURN;
}
Item_allany_subselect::Item_allany_subselect(Item * left_exp,
Comp_creator *fn,
st_select_lex *select_lex,
bool all_arg)
:Item_in_subselect(), all(all_arg)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
left_expr= left_exp;
func= fn;
init(select_lex, new select_exists_subselect(this));
max_columns= 1;
abort_on_null= 0;
reset();
//if test_limit will fail then error will be reported to client
test_limit(select_lex->master_unit());
DBUG_VOID_RETURN;
}
void Item_exists_subselect::fix_length_and_dec()
{
decimals= 0;
max_length= 1;
max_columns= engine->cols();
}
double Item_exists_subselect::val ()
{
if (exec())
{
reset();
return 0;
}
return (double) value;
}
longlong Item_exists_subselect::val_int ()
{
if (exec())
{
reset();
return 0;
}
return value;
}
String *Item_exists_subselect::val_str(String *str)
{
if (exec())
{
reset();
return 0;
}
str->set(value,default_charset());
return str;
}
double Item_in_subselect::val ()
{
if (exec())
{
reset();
null_value= 1;
return 0;
}
if (was_null && !value)
null_value= 1;
return (double) value;
}
longlong Item_in_subselect::val_int ()
{
if (exec())
{
reset();
null_value= 1;
return 0;
}
if (was_null && !value)
null_value= 1;
return value;
}
String *Item_in_subselect::val_str(String *str)
{
if (exec())
{
reset();
null_value= 1;
return 0;
}
if (was_null && !value)
{
null_value= 1;
return 0;
}
str->set(value,default_charset());
return str;
}
Item_subselect::trans_res
Item_in_subselect::single_value_transformer(JOIN *join,
Comp_creator *func)
{
DBUG_ENTER("Item_in_subselect::single_value_transformer");
SELECT_LEX *select_lex= join->select_lex;
THD *thd_tmp= join->thd;
thd_tmp->where= "scalar IN/ALL/ANY subquery";
if (select_lex->item_list.elements > 1)
{
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
DBUG_RETURN(RES_ERROR);
}
if ((abort_on_null || (upper_not && upper_not->top_level())) &&
!select_lex->master_unit()->uncacheable && !func->eqne_op())
{
if (substitution)
{
// It is second (third, ...) SELECT of UNION => All is done
DBUG_RETURN(RES_OK);
}
Item *subs;
if (!select_lex->group_list.elements &&
!select_lex->with_sum_func &&
!(select_lex->next_select()))
{
Item *item;
subs_type type= substype();
if (func->l_op())
{
/*
(ALL && (> || =>)) || (ANY && (< || =<))
for ALL condition is inverted
*/
item= new Item_sum_max(*select_lex->ref_pointer_array);
}
else
{
/*
(ALL && (< || =<)) || (ANY && (> || =>))
for ALL condition is inverted
*/
item= new Item_sum_min(*select_lex->ref_pointer_array);
}
*select_lex->ref_pointer_array= item;
select_lex->item_list.empty();
select_lex->item_list.push_back(item);
if (item->fix_fields(thd_tmp, join->tables_list, &item))
{
DBUG_RETURN(RES_ERROR);
}
subs= new Item_singlerow_subselect(select_lex);
}
else
{
// remove LIMIT placed by ALL/ANY subquery
select_lex->master_unit()->global_parameters->select_limit=
HA_POS_ERROR;
subs= new Item_maxmin_subselect(this, select_lex, func->l_op());
}
// left expression belong to outer select
SELECT_LEX *current= thd_tmp->lex.current_select, *up;
thd_tmp->lex.current_select= up= current->return_after_parsing();
if (left_expr->fix_fields(thd_tmp, up->get_table_list(), &left_expr))
{
thd_tmp->lex.current_select= current;
DBUG_RETURN(RES_ERROR);
}
thd_tmp->lex.current_select= current;
substitution= func->create(left_expr, subs);
DBUG_RETURN(RES_OK);
}
if (!substitution)
{
//first call for this unit
SELECT_LEX_UNIT *unit= select_lex->master_unit();
substitution= optimizer= new Item_in_optimizer(left_expr, this);
SELECT_LEX *current= thd_tmp->lex.current_select, *up;
thd_tmp->lex.current_select= up= current->return_after_parsing();
//optimizer never use Item **ref => we can pass 0 as parameter
if (!optimizer || optimizer->fix_left(thd_tmp, up->get_table_list(), 0))
{
thd_tmp->lex.current_select= current;
DBUG_RETURN(RES_ERROR);
}
thd_tmp->lex.current_select= current;
/*
As far as Item_ref_in_optimizer do not substitude itself on fix_fields
we can use same item for all selects.
*/
expr= new Item_ref((Item**)optimizer->get_cache(),
(char *)"<no matter>",
(char *)in_left_expr_name);
unit->uncacheable|= UNCACHEABLE_DEPENDENT;
}
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
Item *item;
item= (Item*) select_lex->item_list.head();
if (join->having || select_lex->with_sum_func ||
select_lex->group_list.elements)
{
item= func->create(expr,
new Item_ref_null_helper(this,
select_lex->ref_pointer_array,
(char *)"<ref>",
this->full_name()));
join->having= and_items(join->having, item);
select_lex->having_fix_field= 1;
if (join->having->fix_fields(thd_tmp, join->tables_list, &join->having))
{
select_lex->having_fix_field= 0;
DBUG_RETURN(RES_ERROR);
}
select_lex->having_fix_field= 0;
}
else
{
select_lex->item_list.empty();
select_lex->item_list.push_back(new Item_int("Not_used",
(longlong) 1, 21));
select_lex->ref_pointer_array[0]= select_lex->item_list.head();
if (select_lex->table_list.elements)
{
Item *having= item, *isnull= item;
item= func->create(expr, item);
if (!abort_on_null)
{
having= new Item_is_not_null_test(this, having);
join->having= (join->having ?
new Item_cond_and(having, join->having) :
having);
select_lex->having_fix_field= 1;
if (join->having->fix_fields(thd_tmp, join->tables_list,
&join->having))
{
select_lex->having_fix_field= 0;
DBUG_RETURN(RES_ERROR);
}
select_lex->having_fix_field= 0;
item= new Item_cond_or(item,
new Item_func_isnull(isnull));
}
item->name= (char *)in_additional_cond;
join->conds= and_items(join->conds, item);
if (join->conds->fix_fields(thd_tmp, join->tables_list, &join->conds))
DBUG_RETURN(RES_ERROR);
}
else
{
if (select_lex->master_unit()->first_select()->next_select())
{
join->having= func->create(expr,
new Item_null_helper(this, item,
(char *)"<no matter>",
(char *)"<result>"));
select_lex->having_fix_field= 1;
if (join->having->fix_fields(thd_tmp, join->tables_list,
&join->having))
{
select_lex->having_fix_field= 0;
DBUG_RETURN(RES_ERROR);
}
select_lex->having_fix_field= 0;
}
else
{
// it is single select without tables => possible optimization
item= func->create(left_expr, item);
// fix_field of item will be done in time of substituting
substitution= item;
have_to_be_excluded= 1;
if (thd_tmp->lex.describe)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
push_warning(thd_tmp, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SELECT_REDUCED, warn_buff);
}
DBUG_RETURN(RES_REDUCE);
}
}
}
DBUG_RETURN(RES_OK);
}
Item_subselect::trans_res
Item_in_subselect::row_value_transformer(JOIN *join)
{
DBUG_ENTER("Item_in_subselect::row_value_transformer");
THD *thd_tmp= join->thd;
thd_tmp->where= "row IN/ALL/ANY subquery";
SELECT_LEX *select_lex= join->select_lex;
if (select_lex->item_list.elements != left_expr->cols())
{
my_error(ER_OPERAND_COLUMNS, MYF(0), left_expr->cols());
DBUG_RETURN(RES_ERROR);
}
if (!substitution)
{
//first call for this unit
SELECT_LEX_UNIT *unit= select_lex->master_unit();
substitution= optimizer= new Item_in_optimizer(left_expr, this);
SELECT_LEX *current= thd_tmp->lex.current_select, *up;
thd_tmp->lex.current_select= up= current->return_after_parsing();
//optimizer never use Item **ref => we can pass 0 as parameter
if (!optimizer || optimizer->fix_left(thd_tmp, up->get_table_list(), 0))
{
thd_tmp->lex.current_select= current;
DBUG_RETURN(RES_ERROR);
}
thd_tmp->lex.current_select= current;
unit->uncacheable|= UNCACHEABLE_DEPENDENT;
}
uint n= left_expr->cols();
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
select_lex->setup_ref_array(thd_tmp,
select_lex->order_list.elements +
select_lex->group_list.elements);
Item *item= 0;
List_iterator_fast<Item> li(select_lex->item_list);
for (uint i= 0; i < n; i++)
{
Item *func= new Item_ref_null_helper(this,
select_lex->ref_pointer_array+i,
(char *) "<no matter>",
(char *) "<list ref>");
func=
eq_creator.create(new Item_ref((*optimizer->get_cache())->
addr(i),
(char *)"<no matter>",
(char *)in_left_expr_name),
func);
item= and_items(item, func);
}
if (join->having || select_lex->with_sum_func ||
select_lex->group_list.first ||
!select_lex->table_list.elements)
{
join->having= and_items(join->having, item);
select_lex->having_fix_field= 1;
if (join->having->fix_fields(thd_tmp, join->tables_list, &join->having))
{
select_lex->having_fix_field= 0;
DBUG_RETURN(RES_ERROR);
}
select_lex->having_fix_field= 0;
}
else
{
join->conds= and_items(join->conds, item);
if (join->conds->fix_fields(thd_tmp, join->tables_list, &join->having))
DBUG_RETURN(RES_ERROR);
}
DBUG_RETURN(RES_OK);
}
Item_subselect::trans_res
Item_in_subselect::select_transformer(JOIN *join)
{
transformed= 1;
if (left_expr->cols() == 1)
return single_value_transformer(join, &eq_creator);
return row_value_transformer(join);
}
void Item_in_subselect::print(String *str)
{
if (transformed)
str->append("<exists>", 8);
else
{
left_expr->print(str);
str->append(" in ", 4);
}
Item_subselect::print(str);
}
Item_subselect::trans_res
Item_allany_subselect::select_transformer(JOIN *join)
{
transformed= 1;
if (upper_not)
upper_not->show= 1;
return single_value_transformer(join, func);
}
void Item_allany_subselect::print(String *str)
{
if (transformed)
str->append("<exists>", 8);
else
{
left_expr->print(str);
str->append(' ');
str->append(func->symbol(all));
str->append(all ? " all " : " any ", 5);
}
Item_subselect::print(str);
}
subselect_single_select_engine::
subselect_single_select_engine(st_select_lex *select,
select_subselect *result,
Item_subselect *item)
:subselect_engine(item, result),
prepared(0), optimized(0), executed(0), join(0)
{
select_lex= select;
SELECT_LEX_UNIT *unit= select_lex->master_unit();
unit->offset_limit_cnt= unit->global_parameters->offset_limit;
unit->select_limit_cnt= unit->global_parameters->select_limit+
unit->global_parameters ->offset_limit;
if (unit->select_limit_cnt < unit->global_parameters->select_limit)
unit->select_limit_cnt= HA_POS_ERROR; // no limit
if (unit->select_limit_cnt == HA_POS_ERROR)
select_lex->options&= ~OPTION_FOUND_ROWS;
unit->item= item;
this->select_lex= select_lex;
}
subselect_union_engine::subselect_union_engine(st_select_lex_unit *u,
select_subselect *result_arg,
Item_subselect *item_arg)
:subselect_engine(item_arg, result_arg)
{
unit= u;
if (!result_arg) //out of memory
current_thd->fatal_error();
unit->item= item_arg;
}
int subselect_single_select_engine::prepare()
{
if (prepared)
return 0;
join= new JOIN(thd, select_lex->item_list, select_lex->options, result);
if (!join || !result)
{
thd->fatal_error(); //out of memory
return 1;
}
prepared= 1;
SELECT_LEX *save_select= thd->lex.current_select;
thd->lex.current_select= select_lex;
if (join->prepare(&select_lex->ref_pointer_array,
(TABLE_LIST*) select_lex->table_list.first,
select_lex->with_wild,
select_lex->where,
select_lex->order_list.elements +
select_lex->group_list.elements,
(ORDER*) select_lex->order_list.first,
(ORDER*) select_lex->group_list.first,
select_lex->having,
(ORDER*) 0, select_lex,
select_lex->master_unit()))
return 1;
thd->lex.current_select= save_select;
return 0;
}
int subselect_union_engine::prepare()
{
return unit->prepare(thd, result);
}
int subselect_uniquesubquery_engine::prepare()
{
//this never should be called
DBUG_ASSERT(0);
return 1;
}
static Item_result set_row(List<Item> &item_list, Item *item,
Item_cache **row, bool *maybe_null)
{
Item_result res_type= STRING_RESULT;
Item *sel_item;
List_iterator_fast<Item> li(item_list);
for (uint i= 0; (sel_item= li++); i++)
{
item->max_length= sel_item->max_length;
res_type= sel_item->result_type();
item->decimals= sel_item->decimals;
*maybe_null= sel_item->maybe_null;
if (row)
{
if (!(row[i]= Item_cache::get_cache(res_type)))
return STRING_RESULT; // we should return something
row[i]->set_len_n_dec(sel_item->max_length, sel_item->decimals);
row[i]->collation.set(sel_item->collation);
}
}
if (item_list.elements > 1)
res_type= ROW_RESULT;
return res_type;
}
void subselect_single_select_engine::fix_length_and_dec(Item_cache **row)
{
DBUG_ASSERT(row || select_lex->item_list.elements==1);
res_type= set_row(select_lex->item_list, item, row, &maybe_null);
item->collation.set(row[0]->collation);
if (cols() != 1)
maybe_null= 0;
}
void subselect_union_engine::fix_length_and_dec(Item_cache **row)
{
DBUG_ASSERT(row || unit->first_select()->item_list.elements==1);
if (unit->first_select()->item_list.elements == 1)
res_type= set_row(unit->types, item, row, &maybe_null);
else
{
bool fake= 0;
res_type= set_row(unit->types, item, row, &fake);
}
}
void subselect_uniquesubquery_engine::fix_length_and_dec(Item_cache **row)
{
//this never should be called
DBUG_ASSERT(0);
}
int subselect_single_select_engine::exec()
{
DBUG_ENTER("subselect_single_select_engine::exec");
char const *save_where= join->thd->where;
SELECT_LEX *save_select= join->thd->lex.current_select;
join->thd->lex.current_select= select_lex;
if (!optimized)
{
optimized=1;
if (join->optimize())
{
join->thd->where= save_where;
executed= 1;
join->thd->lex.current_select= save_select;
DBUG_RETURN(join->error?join->error:1);
}
if (item->engine_changed)
{
DBUG_RETURN(1);
}
}
if (select_lex->uncacheable && executed)
{
if (join->reinit())
{
join->thd->where= save_where;
join->thd->lex.current_select= save_select;
DBUG_RETURN(1);
}
item->reset();
item->assigned((executed= 0));
}
if (!executed)
{
join->exec();
executed= 1;
join->thd->where= save_where;
join->thd->lex.current_select= save_select;
DBUG_RETURN(join->error||thd->is_fatal_error);
}
join->thd->where= save_where;
join->thd->lex.current_select= save_select;
DBUG_RETURN(0);
}
int subselect_union_engine::exec()
{
char const *save_where= unit->thd->where;
int res= unit->exec();
unit->thd->where= save_where;
return res;
}
int subselect_uniquesubquery_engine::exec()
{
DBUG_ENTER("subselect_uniquesubquery_engine::exec");
int error;
TABLE *table= tab->table;
if ((tab->ref.key_err= (*tab->ref.key_copy)->copy()))
{
table->status= STATUS_NOT_FOUND;
error= -1;
}
else
{
error= table->file->index_read(table->record[0],
tab->ref.key_buff,
tab->ref.key_length,HA_READ_KEY_EXACT);
if (error && error != HA_ERR_KEY_NOT_FOUND)
error= report_error(table, error);
else
{
error= 0;
table->null_row= 0;
((Item_in_subselect *) item)->value= (!table->status &&
(!cond || cond->val_int()) ? 1 :
0);
}
}
DBUG_RETURN(error != 0);
}
subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine()
{
/* Tell handler we don't need the index anymore */
tab->table->file->index_end();
}
int subselect_indexsubquery_engine::exec()
{
DBUG_ENTER("subselect_indexsubselect_engine::exec");
int error;
bool null_finding= 0;
TABLE *table= tab->table;
((Item_in_subselect *) item)->value= 0;
if (check_null)
{
/* We need to check for NULL if there wasn't a matching value */
*tab->null_ref_key= 0; // Search first for not null
((Item_in_subselect *) item)->was_null= 0;
}
if ((*tab->ref.key_copy) && (tab->ref.key_err= (*tab->ref.key_copy)->copy()))
{
table->status= STATUS_NOT_FOUND;
error= -1;
}
else
{
error= table->file->index_read(table->record[0],
tab->ref.key_buff,
tab->ref.key_length,HA_READ_KEY_EXACT);
if (error && error != HA_ERR_KEY_NOT_FOUND)
error= report_error(table, error);
else
{
for (;;)
{
error= 0;
table->null_row= 0;
if (!table->status)
{
if (!cond || cond->val_int())
{
if (null_finding)
((Item_in_subselect *) item)->was_null= 1;
else
((Item_in_subselect *) item)->value= 1;
break;
}
error= table->file->index_next_same(table->record[0],
tab->ref.key_buff,
tab->ref.key_length);
if (error && error != HA_ERR_END_OF_FILE)
{
error= report_error(table, error);
break;
}
}
else
{
if (!check_null || null_finding)
break; /* We don't need to check nulls */
*tab->null_ref_key= 1;
null_finding= 1;
/* Check if there exists a row with a null value in the index */
if ((error= (safe_index_read(tab) == 1)))
break;
}
}
}
}
DBUG_RETURN(error != 0);
}
uint subselect_single_select_engine::cols()
{
return select_lex->item_list.elements;
}
uint subselect_union_engine::cols()
{
return unit->first_select()->item_list.elements;
}
uint8 subselect_single_select_engine::uncacheable()
{
return select_lex->uncacheable;
}
uint8 subselect_union_engine::uncacheable()
{
return unit->uncacheable;
}
void subselect_single_select_engine::exclude()
{
select_lex->master_unit()->exclude_level();
}
void subselect_union_engine::exclude()
{
unit->exclude_level();
}
void subselect_uniquesubquery_engine::exclude()
{
//this never should be called
DBUG_ASSERT(0);
}
table_map subselect_engine::calc_const_tables(TABLE_LIST *table)
{
table_map map= 0;
for(; table; table= table->next)
{
TABLE *tbl= table->table;
if (tbl && tbl->const_table)
map|= tbl->map;
}
return map;
}
table_map subselect_single_select_engine::upper_select_const_tables()
{
return calc_const_tables((TABLE_LIST *) select_lex->outer_select()->
table_list.first);
}
table_map subselect_union_engine::upper_select_const_tables()
{
return calc_const_tables((TABLE_LIST *) unit->outer_select()->
table_list.first);
}
void subselect_single_select_engine::print(String *str)
{
select_lex->print(thd, str);
}
void subselect_union_engine::print(String *str)
{
unit->print(str);
}
void subselect_uniquesubquery_engine::print(String *str)
{
str->append("<primary_index_lookup>(", 23);
tab->ref.items[0]->print(str);
str->append(" in ", 4);
str->append(tab->table->real_name);
KEY *key_info= tab->table->key_info+ tab->ref.key;
str->append(" on ", 4);
str->append(key_info->name);
if (cond)
{
str->append(" where ", 7);
cond->print(str);
}
str->append(')');
}
void subselect_indexsubquery_engine::print(String *str)
{
str->append("<index_lookup>(", 15);
tab->ref.items[0]->print(str);
str->append(" in ", 4);
str->append(tab->table->real_name);
KEY *key_info= tab->table->key_info+ tab->ref.key;
str->append(" on ", 4);
str->append(key_info->name);
if (check_null)
str->append(" chicking NULL", 14);
if (cond)
{
str->append(" where ", 7);
cond->print(str);
}
str->append(')');
}