1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-05-08 15:01:34 +03:00
mariadb-columnstore-engine/dbcon/mysql/ha_mcs_opt_rewrites.cpp
Gagan Goel e257570b04 MCOL-4282 Follow up on the previous commit.
Activate statement arena for:
  1. disable_indices_for_CEJ()
  2. in_subselect_rewrite() for prepared statements
2020-09-23 15:45:20 -04:00

394 lines
13 KiB
C++

/* Copyright (C) 2019-20 MariaDB Corporation
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; version 2 of
the License.
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., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA. */
#include <typeinfo>
#include "ha_mcs_opt_rewrites.h"
// Search simplify_joins() function in the server's code for detail
COND *
simplify_joins_mcs(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
bool in_sj)
{
TABLE_LIST *table;
NESTED_JOIN *nested_join;
TABLE_LIST *prev_table= 0;
List_iterator<TABLE_LIST> li(*join_list);
bool straight_join= MY_TEST(join->select_options & SELECT_STRAIGHT_JOIN);
DBUG_ENTER("simplify_joins_mcs");
/*
Try to simplify join operations from join_list.
The most outer join operation is checked for conversion first.
*/
while ((table= li++))
{
table_map used_tables;
table_map not_null_tables= (table_map) 0;
if ((nested_join= table->nested_join))
{
/*
If the element of join_list is a nested join apply
the procedure to its nested join list first.
*/
if (table->on_expr)
{
Item *expr= table->on_expr;
/*
If an on expression E is attached to the table,
check all null rejected predicates in this expression.
If such a predicate over an attribute belonging to
an inner table of an embedded outer join is found,
the outer join is converted to an inner join and
the corresponding on expression is added to E.
*/
expr= simplify_joins_mcs(join, &nested_join->join_list,
expr, FALSE, in_sj || table->sj_on_expr);
if (!table->prep_on_expr || expr != table->on_expr)
{
DBUG_ASSERT(expr);
table->on_expr= expr;
table->prep_on_expr= expr->copy_andor_structure(join->thd);
}
}
nested_join->used_tables= (table_map) 0;
nested_join->not_null_tables=(table_map) 0;
conds= simplify_joins_mcs(join, &nested_join->join_list, conds, top,
in_sj || table->sj_on_expr);
used_tables= nested_join->used_tables;
not_null_tables= nested_join->not_null_tables;
/* The following two might become unequal after table elimination: */
nested_join->n_tables= nested_join->join_list.elements;
}
else
{
if (!table->prep_on_expr)
table->prep_on_expr= table->on_expr;
used_tables= table->get_map();
if (conds)
not_null_tables= conds->not_null_tables();
}
if (table->embedding)
{
table->embedding->nested_join->used_tables|= used_tables;
table->embedding->nested_join->not_null_tables|= not_null_tables;
}
if (!(table->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT)) ||
(used_tables & not_null_tables))
{
/*
For some of the inner tables there are conjunctive predicates
that reject nulls => the outer join can be replaced by an inner join.
*/
if (table->outer_join && !table->embedding && table->table)
table->table->maybe_null= FALSE;
table->outer_join= 0;
if (!(straight_join || table->straight))
{
table->dep_tables= 0;
TABLE_LIST *embedding= table->embedding;
while (embedding)
{
if (embedding->nested_join->join_list.head()->outer_join)
{
if (!embedding->sj_subq_pred)
table->dep_tables= embedding->dep_tables;
break;
}
embedding= embedding->embedding;
}
}
if (table->on_expr)
{
/* Add ON expression to the WHERE or upper-level ON condition. */
if (conds)
{
conds= and_conds(join->thd, conds, table->on_expr);
conds->top_level_item();
/* conds is always a new item as both cond and on_expr existed */
DBUG_ASSERT(!conds->is_fixed());
conds->fix_fields(join->thd, &conds);
}
else
conds= table->on_expr;
table->prep_on_expr= table->on_expr= 0;
}
}
/*
Only inner tables of non-convertible outer joins
remain with on_expr.
*/
if (table->on_expr)
{
table_map table_on_expr_used_tables= table->on_expr->used_tables();
table->dep_tables|= table_on_expr_used_tables;
if (table->embedding)
{
table->dep_tables&= ~table->embedding->nested_join->used_tables;
/*
Embedding table depends on tables used
in embedded on expressions.
*/
table->embedding->on_expr_dep_tables|= table_on_expr_used_tables;
}
else
table->dep_tables&= ~table->get_map();
}
if (prev_table)
{
/* The order of tables is reverse: prev_table follows table */
if (prev_table->straight || straight_join)
prev_table->dep_tables|= used_tables;
if (prev_table->on_expr)
{
prev_table->dep_tables|= table->on_expr_dep_tables;
table_map prev_used_tables= prev_table->nested_join ?
prev_table->nested_join->used_tables :
prev_table->get_map();
/*
If on expression contains only references to inner tables
we still make the inner tables dependent on the outer tables.
It would be enough to set dependency only on one outer table
for them. Yet this is really a rare case.
Note:
RAND_TABLE_BIT mask should not be counted as it
prevents update of inner table dependences.
For example it might happen if RAND() function
is used in JOIN ON clause.
*/
if (!((prev_table->on_expr->used_tables() &
~(OUTER_REF_TABLE_BIT | RAND_TABLE_BIT)) &
~prev_used_tables))
prev_table->dep_tables|= used_tables;
}
}
prev_table= table;
}
/*
Flatten nested joins that can be flattened.
no ON expression and not a semi-join => can be flattened.
*/
li.rewind();
while ((table= li++))
{
nested_join= table->nested_join;
if (table->sj_on_expr && !in_sj)
{
/*
If this is a semi-join that is not contained within another semi-join
leave it intact (otherwise it is flattened)
*/
/*
Make sure that any semi-join appear in
the join->select_lex->sj_nests list only once
*/
List_iterator_fast<TABLE_LIST> sj_it(join->select_lex->sj_nests);
TABLE_LIST *sj_nest;
while ((sj_nest= sj_it++))
{
if (table == sj_nest)
break;
}
if (sj_nest)
continue;
join->select_lex->sj_nests.push_back(table, join->thd->mem_root);
/*
Also, walk through semi-join children and mark those that are now
top-level
*/
TABLE_LIST *tbl;
List_iterator<TABLE_LIST> it(nested_join->join_list);
while ((tbl= it++))
{
if (!tbl->on_expr && tbl->table)
tbl->table->maybe_null= FALSE;
}
}
else if (nested_join && !table->on_expr)
{
TABLE_LIST *tbl;
List_iterator<TABLE_LIST> it(nested_join->join_list);
List<TABLE_LIST> repl_list;
while ((tbl= it++))
{
tbl->embedding= table->embedding;
if (!tbl->embedding && !tbl->on_expr && tbl->table)
tbl->table->maybe_null= FALSE;
tbl->join_list= table->join_list;
repl_list.push_back(tbl, join->thd->mem_root);
tbl->dep_tables|= table->dep_tables;
}
li.replace(repl_list);
}
}
DBUG_RETURN(conds);
}
/*@brief in_subselect_rewrite_walk - Rewrites Item_in_subselect*/
/************************************************************
* DESCRIPTION:
* It traverses filter predicates searching for
* Item_in_subselect and rewrites it adding equi-join predicate
* to finalise IN_2_EXISTS rewrite.
* PARAMETERS:
* item_arg - Item to check.
* arg - bool to early return if predicate injection fails.
* RETURN:
***********************************************************/
void in_subselect_rewrite_walk(const Item* item_arg, void* arg)
{
bool* result= reinterpret_cast<bool*>(arg);
if (*result) return;
Item* item= const_cast<Item*>(item_arg);
JOIN* join= nullptr;
if (typeid(*item) == typeid(Item_in_subselect))
{
Item_in_subselect* sub= reinterpret_cast<Item_in_subselect*>(item);
// MCS 1.4.3 doesn't support IN + subquery with UNION so
// we safe to take this JOIN.
join= sub->unit->first_select()->join;
// Inject equi-JOIN predicates if needed.
*result= sub->create_in_to_exists_cond(join);
*result= (*result) ? *result :
sub->inject_in_to_exists_cond(join);
sub->unit->first_select()->prep_where=
join->conds ? join->conds->copy_andor_structure(current_thd) : 0;
}
else if (typeid(*item) == typeid(Item_singlerow_subselect))
{
Item_singlerow_subselect* sub=
reinterpret_cast<Item_singlerow_subselect*>(item);
// MCS 1.4.3 doesn't support IN + subquery with UNION so
// we safe to take this JOIN.
join= sub->unit->first_select()->join;
}
else
{
// Exit for any but dedicated Items.
return;
}
// Walk recursively to process nested IN ops.
if (join->conds)
{
join->conds->traverse_cond(in_subselect_rewrite_walk,
arg, Item::POSTFIX);
}
}
/* @brief opt_flag_unset_PS() - Unsets first_cond_optimization */
/************************************************************
* DESCRIPTION:
* This function traverses derived tables to unset
* SELECT_LEX::first_cond_optimization: a marker to control
* optimizations executing PS. If set it allows to apply
* optimizations. If unset, it disables optimizations.
* PARAMETERS:
* select_lex - SELECT_LEX* that describes the query.
***********************************************************/
void opt_flag_unset_PS(SELECT_LEX *select_lex)
{
TABLE_LIST *tbl;
List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
while ((tbl= li++))
{
if (tbl->is_view_or_derived())
{
SELECT_LEX_UNIT *unit= tbl->get_unit();
for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
opt_flag_unset_PS(sl);
}
}
if (select_lex->first_cond_optimization)
{
select_lex->first_cond_optimization= false;
}
}
/* @brief in_subselect_rewrite - Rewrites Item_in_subselect */
/************************************************************
* DESCRIPTION:
* This function traverses TABLE_LISTs running in_subselect_rewrite_walk
* PARAMETERS:
* select_lex - SELECT_LEX* that describes the query.
* RETURN:
* bool to indicate predicate injection failures.
***********************************************************/
bool in_subselect_rewrite(SELECT_LEX *select_lex)
{
bool result = false;
TABLE_LIST *tbl;
List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
while (!result && (tbl = li++))
{
if (tbl->is_view_or_derived())
{
SELECT_LEX_UNIT *unit= tbl->get_unit();
for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
result = in_subselect_rewrite(sl);
}
}
if (select_lex->join && select_lex->join->conds)
{
select_lex->join->conds->traverse_cond(in_subselect_rewrite_walk, &result,
Item::POSTFIX);
}
return result;
}
uint build_bitmap_for_nested_joins_mcs(List<TABLE_LIST> *join_list,
uint first_unused)
{
List_iterator<TABLE_LIST> li(*join_list);
TABLE_LIST *table;
DBUG_ENTER("build_bitmap_for_nested_joins_mcs");
while ((table= li++))
{
NESTED_JOIN *nested_join;
if ((nested_join= table->nested_join))
{
if (nested_join->n_tables != 1)
{
/* Don't assign bits to sj-nests */
if (table->on_expr)
nested_join->nj_map= (nested_join_map) 1 << first_unused++;
first_unused= build_bitmap_for_nested_joins_mcs(&nested_join->join_list,
first_unused);
}
}
}
DBUG_RETURN(first_unused);
}