1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

Fixed mdev-14093 Wrong result upon JOIN with INDEX with no rows

in joined table + GROUP BY + GROUP_CONCAT + HAVING + ORDER BY
[by field from HAVING] + 1 row expected

The fix is actually a port of the fix for bug #17055185 from
mysql code line (see commit f289aeeef0743508ff87211084453b3b88a6d017
by Mithun C Y into mysql-5.6). The test case for the bug #17055185
was also ported.
This commit is contained in:
Igor Babaev
2017-11-02 15:56:18 -07:00
parent b0cfb16867
commit beac522b55
4 changed files with 261 additions and 76 deletions

View File

@ -2197,6 +2197,76 @@ derived_exit:
DBUG_RETURN(0);
}
/**
Add having condition as a where clause condition of the given temp table.
@param tab Table to which having condition is added.
@returns false if success, true if error.
*/
bool JOIN::add_having_as_table_cond(JOIN_TAB *tab)
{
tmp_having->update_used_tables();
table_map used_tables= tab->table->map | OUTER_REF_TABLE_BIT;
/* If tmp table is not used then consider conditions of const table also */
if (!need_tmp)
used_tables|= const_table_map;
DBUG_ENTER("JOIN::add_having_as_table_cond");
Item* sort_table_cond= make_cond_for_table(thd, tmp_having, used_tables,
(table_map) 0, false,
false, false);
if (sort_table_cond)
{
if (!tab->select)
{
if (!(tab->select= new SQL_SELECT))
DBUG_RETURN(true);
tab->select->head= tab->table;
}
if (!tab->select->cond)
tab->select->cond= sort_table_cond;
else
{
if (!(tab->select->cond=
new (thd->mem_root) Item_cond_and(thd,
tab->select->cond,
sort_table_cond)))
DBUG_RETURN(true);
}
if (tab->pre_idx_push_select_cond)
{
if (sort_table_cond->type() == Item::COND_ITEM)
sort_table_cond= sort_table_cond->copy_andor_structure(thd);
if (!(tab->pre_idx_push_select_cond=
new (thd->mem_root) Item_cond_and(thd,
tab->pre_idx_push_select_cond,
sort_table_cond)))
DBUG_RETURN(true);
}
if (tab->select->cond && !tab->select->cond->fixed)
tab->select->cond->fix_fields(thd, 0);
if (tab->pre_idx_push_select_cond && !tab->pre_idx_push_select_cond->fixed)
tab->pre_idx_push_select_cond->fix_fields(thd, 0);
tab->select->pre_idx_push_select_cond= tab->pre_idx_push_select_cond;
tab->set_select_cond(tab->select->cond, __LINE__);
tab->select_cond->top_level_item();
DBUG_EXECUTE("where",print_where(tab->select->cond,
"select and having",
QT_ORDINARY););
having= make_cond_for_table(thd, tmp_having, ~ (table_map) 0,
~used_tables, false, false, false);
DBUG_EXECUTE("where",
print_where(having, "having after sort", QT_ORDINARY););
}
DBUG_RETURN(false);
}
/**
Set info for aggregation tables
@ -2226,6 +2296,7 @@ bool JOIN::make_aggr_tables_info()
TABLE *exec_tmp_table= NULL;
bool distinct= false;
bool keep_row_order= false;
bool is_having_added_as_table_cond= false;
DBUG_ENTER("JOIN::make_aggr_tables_info");
const bool has_group_by= this->group;
@ -2412,29 +2483,6 @@ bool JOIN::make_aggr_tables_info()
if (exec_tmp_table->distinct)
optimize_distinct();
/*
We don't have to store rows in temp table that doesn't match HAVING if:
- we are sorting the table and writing complete group rows to the
temp table.
- We are using DISTINCT without resolving the distinct as a GROUP BY
on all columns.
If having is not handled here, it will be checked before the row
is sent to the client.
In the case of window functions however, we *must* make sure to not
store any rows which don't match HAVING within the temp table,
as rows will end up being used during their computation.
*/
if (having &&
(sort_and_group || (exec_tmp_table->distinct && !group_list) ||
select_lex->have_window_funcs()))
{
/* Attach HAVING to tmp table's condition */
curr_tab->having= having;
having= NULL; /* Already done */
}
/* Change sum_fields reference to calculated fields in tmp_table */
items1= ref_ptr_array_slice(2);
if ((sort_and_group || curr_tab->table->group ||
@ -2462,6 +2510,38 @@ bool JOIN::make_aggr_tables_info()
curr_tab->fields= &tmp_fields_list1;
set_postjoin_aggr_write_func(curr_tab);
/*
If having is not handled here, it will be checked before the row is sent
to the client.
*/
if (tmp_having &&
(sort_and_group || (exec_tmp_table->distinct && !group_list) ||
select_lex->have_window_funcs()))
{
/*
If there is no select distinct and there are no window functions
then move the having to table conds of tmp table.
NOTE : We cannot apply having after distinct or window functions
If columns of having are not part of select distinct,
then distinct may remove rows which can satisfy having.
In the case of window functions we *must* make sure to not
store any rows which don't match HAVING within the temp table,
as rows will end up being used during their computation.
*/
if (!select_distinct && !select_lex->have_window_funcs() &&
add_having_as_table_cond(curr_tab))
DBUG_RETURN(true);
is_having_added_as_table_cond= tmp_having != having;
/*
Having condition which we are not able to add as tmp table conds are
kept as before. And, this will be applied before storing the rows in
tmp table.
*/
curr_tab->having= having;
having= NULL; // Already done
}
tmp_table_param.func_count= 0;
tmp_table_param.field_count+= tmp_table_param.func_count;
if (sort_and_group || curr_tab->table->group)
@ -2651,60 +2731,11 @@ bool JOIN::make_aggr_tables_info()
DBUG_PRINT("info",("Sorting for send_result_set_metadata"));
THD_STAGE_INFO(thd, stage_sorting_result);
/* If we have already done the group, add HAVING to sorted table */
if (tmp_having && !group_list && !sort_and_group)
if (tmp_having && !is_having_added_as_table_cond &&
!group_list && !sort_and_group)
{
// Some tables may have been const
tmp_having->update_used_tables();
table_map used_tables= (const_table_map | curr_tab->table->map);
Item* sort_table_cond= make_cond_for_table(thd, tmp_having, used_tables,
(table_map) 0, false,
false, false);
if (sort_table_cond)
{
if (!curr_tab->select)
{
if (!(curr_tab->select= new SQL_SELECT))
DBUG_RETURN(true);
curr_tab->select->head= curr_tab->table;
}
if (!curr_tab->select->cond)
curr_tab->select->cond= sort_table_cond;
else
{
if (!(curr_tab->select->cond=
new (thd->mem_root) Item_cond_and(thd, curr_tab->select->cond,
sort_table_cond)))
DBUG_RETURN(true);
}
if (curr_tab->pre_idx_push_select_cond)
{
if (sort_table_cond->type() == Item::COND_ITEM)
sort_table_cond= sort_table_cond->copy_andor_structure(thd);
if (!(curr_tab->pre_idx_push_select_cond=
new (thd->mem_root) Item_cond_and(thd,
curr_tab->pre_idx_push_select_cond,
sort_table_cond)))
DBUG_RETURN(true);
}
if (curr_tab->select->cond && !curr_tab->select->cond->fixed)
curr_tab->select->cond->fix_fields(thd, 0);
if (curr_tab->pre_idx_push_select_cond &&
!curr_tab->pre_idx_push_select_cond->fixed)
curr_tab->pre_idx_push_select_cond->fix_fields(thd, 0);
curr_tab->select->pre_idx_push_select_cond=
curr_tab->pre_idx_push_select_cond;
curr_tab->set_select_cond(curr_tab->select->cond, __LINE__);
curr_tab->select_cond->top_level_item();
DBUG_EXECUTE("where",print_where(curr_tab->select->cond,
"select and having",
QT_ORDINARY););
having= make_cond_for_table(thd, tmp_having, ~ (table_map) 0,
~used_tables, false, false, false);
DBUG_EXECUTE("where",
print_where(having, "having after sort", QT_ORDINARY););
}
if (add_having_as_table_cond(curr_tab))
DBUG_RETURN(true);
}
if (group)