summaryrefslogtreecommitdiff
path: root/sql/item_subselect.cc
diff options
context:
space:
mode:
authorunknown <sergefp@mysql.com>2007-01-12 23:22:41 +0300
committerunknown <sergefp@mysql.com>2007-01-12 23:22:41 +0300
commit5f97dc6e9e0b21df249f3c2fa26d8f7616797097 (patch)
tree89dced7ceab3b8f0b6983f1ecea0d1c59eedf0a1 /sql/item_subselect.cc
parentb671815c95bf6c86145c16bde011a4abdd35093d (diff)
downloadmariadb-git-5f97dc6e9e0b21df249f3c2fa26d8f7616797097.tar.gz
BUG#24127: (a,b) IN (SELECT c,d ...) can produce wrong results if a and/or b are NULLs:
- Make the code produce correct result: use an array of triggers to turn on/off equalities for each compared column. Also turn on/off optimizations based on those equalities. - Make EXPLAIN output show "Full scan on NULL key" for tables for which we switch between ref/unique_subquery/index_subquery and ALL access. - index_subquery engine now has HAVING clause when it is needed, and it is displayed in EXPLAIN EXTENDED - Fix incorrect presense of "Using index" for index/unique-based subqueries (BUG#22930) // bk trigger note: this commit refers to BUG#24127 mysql-test/r/ndb_subquery.result: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Updated test results (checked) mysql-test/r/subselect.result: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Updated test results (checked) mysql-test/r/subselect2.result: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Updated test results (checked) mysql-test/r/subselect3.result: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Testcases mysql-test/t/subselect3.test: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Testcases sql/item_cmpfunc.cc: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - For row-based IN subqueries, use one flag per each column. Set the flags appropriately before running the subquery. sql/item_cmpfunc.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Added Item_func_trig_cond::get_triv_var() sql/item_subselect.cc: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Item_subselect::exec() and subselect_*_engine::exec() don't have parameter anymore - now Item_subselect owns the pushed down predicates guard flags. - A correct set of conditional predicates is now pushed into row-based IN subquery. - select_indexsubquery_engine now has "HAVING clause" (needed for correct query results), and it is shown in EXPLAIN EXTENDED sql/item_subselect.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Item_subselect::exec() and subselect_*_engine::exec() don't have parameter anymore - now Item_subselect owns the pushed down predicates guard flags. - A correct set of conditional predicates is now pushed into row-based IN subquery. - select_indexsubquery_engine now has "HAVING clause" (needed for correct query results), and it is shown in EXPLAIN EXTENDED sql/mysql_priv.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Added "in_having_cond" special Item name sql/mysqld.cc: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Added "in_having_cond" special Item name sql/sql_lex.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) sql/sql_select.cc: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Make "ref" analyzer be able to work with conditional equalities - Fix subquery optimization code to match the changes in what kinds of conditions are pushed down into subqueries - Fix wrong EXPLAIN output in some queries with subquery (BUG#22390) sql/sql_select.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Make "ref" analyzer be able to work with conditional equalities - Fix wrong EXPLAIN output in some queries with subquery (BUG#22390)
Diffstat (limited to 'sql/item_subselect.cc')
-rw-r--r--sql/item_subselect.cc267
1 files changed, 156 insertions, 111 deletions
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index e475634a005..7e6a5614c27 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -191,16 +191,16 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
return res;
}
-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);
}
@@ -448,13 +448,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();
@@ -469,7 +469,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();
@@ -483,7 +483,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);
@@ -498,7 +498,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);
@@ -513,7 +513,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();
@@ -565,7 +565,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;
@@ -610,7 +610,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;
@@ -621,7 +621,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;
@@ -632,7 +632,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;
@@ -645,7 +645,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;
@@ -658,7 +658,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;
@@ -676,7 +676,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;
@@ -697,7 +697,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;
@@ -718,7 +718,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;
@@ -738,7 +738,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;
@@ -758,7 +758,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;
@@ -948,19 +948,14 @@ Item_in_subselect::single_value_transformer(JOIN *join,
unit->uncacheable|= UNCACHEABLE_DEPENDENT;
}
+ if (!abort_on_null && left_expr->maybe_null)
+ {
+ 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)
{
@@ -976,9 +971,9 @@ Item_in_subselect::single_value_transformer(JOIN *join,
{
/*
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));
}
/*
@@ -987,6 +982,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)
@@ -1017,7 +1014,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
if (left_expr->maybe_null)
{
if (!(having= new Item_func_trig_cond(having,
- &enable_pushed_conds)))
+ get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
}
/*
@@ -1025,6 +1022,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
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;
/*
@@ -1040,16 +1038,21 @@ Item_in_subselect::single_value_transformer(JOIN *join,
new Item_func_isnull(orig_item));
}
/*
- If we may encounter NULL IN (SELECT ...) and care between NULL and
- FALSE, wrap it in a trigger.
+ 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, &enable_pushed_conds)))
+ 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
@@ -1083,11 +1086,13 @@ Item_in_subselect::single_value_transformer(JOIN *join,
if (!abort_on_null && left_expr->maybe_null)
{
if (!(new_having= new Item_func_trig_cond(new_having,
- &enable_pushed_conds)))
+ 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
@@ -1157,6 +1162,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)
+ {
+ 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;
@@ -1173,6 +1187,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++)
@@ -1201,21 +1216,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);
@@ -1264,18 +1286,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,
@@ -1284,14 +1303,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
@@ -1305,9 +1333,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()
@@ -1693,7 +1721,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;
@@ -1731,9 +1759,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.
@@ -1741,32 +1773,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;
@@ -1778,13 +1814,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;
@@ -1792,7 +1824,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()
@@ -1801,8 +1833,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
@@ -1814,7 +1846,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();
@@ -1907,10 +1938,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
@@ -1918,11 +1952,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())
@@ -1943,9 +1978,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);
@@ -2011,7 +2050,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;
@@ -2052,8 +2091,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
@@ -2196,11 +2236,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(')');
}