1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-07-29 08:21:15 +03:00

MCOL-4665 Move outer join to inner join conversion into the engine.

This is a subtask of MCOL-4525 Implement select_handler=AUTO.

Server performs outer join to inner join conversion using simplify_joins()
in sql/sql_select.cc, by updating the TABLE_LIST::outer_join variable.
In order to perform this conversion, permanent changes are made in some
cases to the SELECT_LEX::JOIN::conds and/or TABLE_LIST::on_expr.
This is undesirable for MCOL-4525 which will attemp to fallback and execute
the query inside the server, in case the query execution fails in ColumnStore
using the select_handler.

For a query such as:
  SELECT * FROM t1 LEFT JOIN t2 ON expr1 LEFT JOIN t3 ON expr2
In some cases, server can update the original SELECT_LEX::JOIN::conds
and/or TABLE_LIST::on_expr and create new Item_cond_and objects
(e.g. with 2 Item's expr1 and expr2 in Item_cond_and::list).
Instead of making changes to the original query structs, we use
gp_walk_info::tableOnExprList and gp_walk_info::condList. 2 Item's,
expr1 and expr2, in the condList, mean Item_cond_and(expr1, expr2), and
hence avoid permanent transformations to the SELECT_LEX.

We also define a new member variable
ha_columnstore_select_handler::tableOuterJoinMap
which saves the original TABLE_LIST::outer_join values before they are
updated. This member variable will be used later on to restore to the original
state of TABLE_LIST::outer_join in case of a query fallback to server execution.

The original simplify_joins() implementation in the server also performs a
flattening of the JOIN nest, however we don't perform this operation in
convertOuterJoinToInnerJoin() since it is not required for ColumnStore.
This commit is contained in:
Gagan Goel
2021-05-17 09:53:23 +00:00
parent 4d835d78d3
commit e0d2a21cb9
9 changed files with 600 additions and 376 deletions

View File

@ -59,6 +59,7 @@ using namespace logging;
#include "ha_mcs_impl_if.h"
#include "ha_mcs_sysvars.h"
#include "ha_subquery.h"
#include "ha_mcs_pushdown.h"
using namespace cal_impl_if;
#include "calpontselectexecutionplan.h"
@ -136,6 +137,142 @@ public:
namespace cal_impl_if
{
// This is taken from Item_cond::fix_fields in sql/item_cmpfunc.cc.
void calculateNotNullTables(const std::vector<COND*>& condList,
table_map& not_null_tables)
{
for (Item* item : condList)
{
if (item->can_eval_in_optimize() && !item->with_sp_var() &&
!cond_has_datetime_is_null(item))
{
if (item->eval_const_cond())
{
}
else
{
not_null_tables = (table_map) 0;
}
}
else
{
not_null_tables |= item->not_null_tables();
}
}
}
// Recursively iterate through the join_list and store all non-null
// TABLE_LIST::on_expr items to a hash map keyed by the TABLE_LIST ptr.
// This is then used by convertOuterJoinToInnerJoin().
void buildTableOnExprList(List<TABLE_LIST>* join_list,
TableOnExprList& tableOnExprList)
{
TABLE_LIST *table;
NESTED_JOIN *nested_join;
List_iterator<TABLE_LIST> li(*join_list);
while ((table = li++))
{
if ((nested_join = table->nested_join))
{
buildTableOnExprList(&nested_join->join_list, tableOnExprList);
}
if (table->on_expr)
tableOnExprList[table].push_back(table->on_expr);
}
}
// This is a trimmed down version of simplify_joins() in sql/sql_select.cc.
// Refer to that function for more details. But we have customized the
// original implementation in simplify_joins() to avoid any changes to
// the SELECT_LEX::JOIN::conds. Specifically, in some cases, simplify_joins()
// would create new Item_cond_and objects. We want to avoid such changes to
// the SELECT_LEX in a scenario where the select_handler execution has failed
// and we want to fallback to the server execution (MCOL-4525). Here, we mimick
// the creation of Item_cond_and using tableOnExprList and condList.
void convertOuterJoinToInnerJoin(List<TABLE_LIST>* join_list,
TableOnExprList& tableOnExprList,
std::vector<COND*>& condList,
TableOuterJoinMap& tableOuterJoinMap)
{
TABLE_LIST *table;
NESTED_JOIN *nested_join;
List_iterator<TABLE_LIST> li(*join_list);
while ((table = li++))
{
table_map used_tables;
table_map not_null_tables = (table_map) 0;
if ((nested_join = table->nested_join))
{
auto iter = tableOnExprList.find(table);
if ((iter != tableOnExprList.end()) && !iter->second.empty())
{
convertOuterJoinToInnerJoin(&nested_join->join_list,
tableOnExprList, tableOnExprList[table], tableOuterJoinMap);
}
nested_join->used_tables = (table_map) 0;
nested_join->not_null_tables = (table_map) 0;
convertOuterJoinToInnerJoin(&nested_join->join_list,
tableOnExprList, condList, tableOuterJoinMap);
used_tables = nested_join->used_tables;
not_null_tables = nested_join->not_null_tables;
}
else
{
used_tables = table->get_map();
if (!condList.empty())
{
if (condList.size() == 1)
{
not_null_tables = condList[0]->not_null_tables();
}
else
{
calculateNotNullTables(condList, 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))
{
if (table->outer_join)
{
tableOuterJoinMap[table] = table->outer_join;
}
table->outer_join = 0;
auto iter = tableOnExprList.find(table);
if (iter != tableOnExprList.end() && !iter->second.empty())
{
// The original implementation in simplify_joins() creates
// an Item_cond_and object here. Instead of doing so, we
// append the table->on_expr to the condList and hence avoid
// making any permanent changes to SELECT_LEX::JOIN::conds.
condList.insert(condList.end(),
tableOnExprList[table].begin(), tableOnExprList[table].end());
iter->second.clear();
}
}
}
}
CalpontSystemCatalog::TableAliasName makeTableAliasName(TABLE_LIST* table)
{
return make_aliasview(
@ -1281,10 +1418,24 @@ uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
if (table->nested_join && !table->derived)
buildJoin(gwi, table->nested_join->join_list, outerJoinStack);
if (table->on_expr)
{
Item_cond* expr = reinterpret_cast<Item_cond*>(table->on_expr);
std::vector<COND*> tableOnExprList;
auto iter = gwi.tableOnExprList.find(table);
// Check if this table's on_expr is available in the hash map
// built/updated during convertOuterJoinToInnerJoin().
if ((iter != gwi.tableOnExprList.end()) && !iter->second.empty())
{
tableOnExprList = iter->second;
}
// This table's on_expr has not been seen/processed before.
else if ((iter == gwi.tableOnExprList.end()) && table->on_expr)
{
tableOnExprList.push_back(table->on_expr);
}
if (!tableOnExprList.empty())
{
if (table->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT))
{
// inner tables block
@ -1312,9 +1463,13 @@ uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
cerr << endl;
cerr << " outer table expression: " << endl;
for (Item* expr : tableOnExprList)
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
#endif
for (Item* expr : tableOnExprList)
{
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
// Error out subquery in outer join on filter for now
@ -1325,6 +1480,7 @@ uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText);
return -1;
}
}
// build outerjoinon filter
ParseTree* filters = NULL, *ptp = NULL, *rhs = NULL;
@ -1355,11 +1511,15 @@ uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
}
}
else // inner join
{
for (Item* expr : tableOnExprList)
{
expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
}
#ifdef DEBUG_WALK_COND
cerr << " inner join expression: " << endl;
for (Item* expr : tableOnExprList)
expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif
}
@ -2667,6 +2827,22 @@ void setError(THD* thd, uint32_t errcode, string errmsg, gp_walk_info& gwi)
clearStacks(gwi);
}
int setErrorAndReturn(gp_walk_info &gwi)
{
// if this is dervied table process phase, mysql may have not developed the plan
// completely. Do not error and eventually mysql will call JOIN::exec() again.
// related to bug 2922. Need to find a way to skip calling rnd_init for derived table
// processing.
if (gwi.thd->derived_tables_processing)
{
gwi.cs_vtable_is_update_with_derive = true;
return -1;
}
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
}
const string bestTableName(const Item_field* ifp)
{
idbassert(ifp);
@ -6364,7 +6540,6 @@ int processFrom(bool &isUnion,
csep->distinctUnionNum(distUnionNum);
}
return 0;
}
@ -6382,24 +6557,24 @@ int processWhere(SELECT_LEX &select_lex,
const std::vector<COND*>& condStack)
{
JOIN* join = select_lex.join;
Item_cond* icp = 0;
Item* icp = 0;
bool isUpdateDelete = false;
// Flag to indicate if this is a prepared statement
bool isPS = gwi.thd->stmt_arena && gwi.thd->stmt_arena->is_stmt_execute();
if (join != 0 && !isPS)
icp = reinterpret_cast<Item_cond*>(join->conds);
icp = join->conds;
else if (isPS && select_lex.prep_where)
icp = (Item_cond*)(select_lex.prep_where);
icp = select_lex.prep_where;
// if icp is null, try to find the where clause other where
if (!join && gwi.thd->lex->derived_tables)
{
if (select_lex.prep_where)
icp = (Item_cond*)(select_lex.prep_where);
icp = select_lex.prep_where;
else if (select_lex.where)
icp = (Item_cond*)(select_lex.where);
icp = select_lex.where;
}
else if (!join && ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) ||
((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) ||
@ -6429,20 +6604,7 @@ int processWhere(SELECT_LEX &select_lex,
if (gwi.fatalParseError)
{
// if this is dervied table process phase, mysql may have not developed the plan
// completely. Do not error and eventually mysql will call JOIN::exec() again.
// related to bug 2922. Need to find a way to skip calling rnd_init for derived table
// processing.
if (gwi.thd->derived_tables_processing)
{
// MCOL-2178 isUnion member only assigned, never used
//MIGR::infinidb_vtable.isUnion = false;
gwi.cs_vtable_is_update_with_derive = true;
return -1;
}
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
return setErrorAndReturn(gwi);
}
}
else if (isUpdateDelete)
@ -6460,33 +6622,19 @@ int processWhere(SELECT_LEX &select_lex,
if (gwi.fatalParseError)
{
if (gwi.thd->derived_tables_processing)
{
gwi.cs_vtable_is_update_with_derive = true;
return -1;
}
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
return setErrorAndReturn(gwi);
}
}
}
// if condStack is empty(), check the select_lex for where conditions
// as a last resort
else if ((icp = reinterpret_cast<Item_cond*>(select_lex.where)) != 0)
else if ((icp = select_lex.where) != 0)
{
icp->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError)
{
if (gwi.thd->derived_tables_processing)
{
gwi.cs_vtable_is_update_with_derive = true;
return -1;
}
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
return setErrorAndReturn(gwi);
}
}
}
@ -6496,6 +6644,19 @@ int processWhere(SELECT_LEX &select_lex,
(dynamic_cast<ConstantColumn*>(gwi.rcWorkStack.top()))->timeZone(gwi.thd->variables.time_zone->get_name()->ptr());
}
for (Item* item : gwi.condList)
{
if (item && (item != icp))
{
item->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError)
{
return setErrorAndReturn(gwi);
}
}
}
// ZZ - the followinig debug shows the structure of nested outer join. should
// use a recursive function.
#ifdef OUTER_JOIN_DEBUG
@ -8352,9 +8513,9 @@ int cp_get_group_plan(THD* thd, SCSEP& csep, cal_impl_if::cal_group_info& gi)
return 0;
}
int cs_get_derived_plan(derived_handler* handler, THD* thd, SCSEP& csep, gp_walk_info& gwi)
int cs_get_derived_plan(ha_columnstore_derived_handler* handler, THD* thd, SCSEP& csep, gp_walk_info& gwi)
{
SELECT_LEX select_lex = *handler->select;
SELECT_LEX& select_lex = *handler->select;
int status = getSelectPlan(gwi, select_lex, csep, false);
if (status > 0)
@ -8372,9 +8533,20 @@ int cs_get_derived_plan(derived_handler* handler, THD* thd, SCSEP& csep, gp_walk
return 0;
}
int cs_get_select_plan(select_handler* handler, THD* thd, SCSEP& csep, gp_walk_info& gwi)
int cs_get_select_plan(ha_columnstore_select_handler* handler, THD* thd, SCSEP& csep, gp_walk_info& gwi)
{
SELECT_LEX select_lex = *handler->select;
SELECT_LEX& select_lex = *handler->select;
if (select_lex.where)
{
gwi.condList.push_back(select_lex.where);
}
buildTableOnExprList(&select_lex.top_join_list, gwi.tableOnExprList);
convertOuterJoinToInnerJoin(&select_lex.top_join_list,
gwi.tableOnExprList, gwi.condList, handler->tableOuterJoinMap);
int status = getSelectPlan(gwi, select_lex, csep, false, true);
if (status > 0)

View File

@ -4679,8 +4679,8 @@ int ha_mcs_impl_pushdown_init(mcs_handler_info* handler_info, TABLE* table)
sm::cpsm_conhdl_t* hndl;
SCSEP csep;
// Declare handlers ptrs in this scope for future use.
select_handler* sh = nullptr;
derived_handler* dh = nullptr;
ha_columnstore_select_handler* sh = nullptr;
ha_columnstore_derived_handler* dh = nullptr;
// update traceFlags according to the autoswitch state.
ci->traceFlags = (ci->traceFlags | CalpontSelectExecutionPlan::TRACE_TUPLE_OFF)^
@ -4774,12 +4774,12 @@ int ha_mcs_impl_pushdown_init(mcs_handler_info* handler_info, TABLE* table)
int status = 42;
if (handler_info->hndl_type == mcs_handler_types_t::SELECT)
{
sh = reinterpret_cast<select_handler*>(handler_info->hndl_ptr);
sh = reinterpret_cast<ha_columnstore_select_handler*>(handler_info->hndl_ptr);
status = cs_get_select_plan(sh, thd, csep, gwi);
}
else if (handler_info->hndl_type == DERIVED)
{
dh = reinterpret_cast<derived_handler*>(handler_info->hndl_ptr);
dh = reinterpret_cast<ha_columnstore_derived_handler*>(handler_info->hndl_ptr);
status = cs_get_derived_plan(dh, thd, csep, gwi);
}

View File

@ -34,6 +34,8 @@
#include "idb_mysql.h"
struct st_ha_create_information;
class ha_columnstore_select_handler;
class ha_columnstore_derived_handler;
#include "configcpp.h"
#include "idberrorinfo.h"
@ -89,7 +91,8 @@ enum ClauseType
typedef std::vector<JoinInfo> JoinInfoVec;
typedef std::map<execplan::CalpontSystemCatalog::TableAliasName, std::pair<int, TABLE_LIST*> > TableMap;
typedef std::tr1::unordered_map<TABLE_LIST*, std::vector<COND*>> TableOnExprList;
typedef std::tr1::unordered_map<TABLE_LIST*, uint> TableOuterJoinMap;
struct gp_walk_info
{
@ -164,6 +167,10 @@ struct gp_walk_info
execplan::ReturnedColumn* inSubQueryLHS;
Item* inSubQueryLHSItem;
// The below 2 fields are required for MCOL-4525.
TableOnExprList tableOnExprList;
std::vector<COND*> condList;
gp_walk_info() : sessionid(0),
fatalParseError(false),
condPush(false),
@ -348,8 +355,8 @@ const std::string infinidb_err_msg = "\nThe query includes syntax that is not su
int cp_get_plan(THD* thd, execplan::SCSEP& csep);
int cp_get_table_plan(THD* thd, execplan::SCSEP& csep, cal_impl_if::cal_table_info& ti);
int cp_get_group_plan(THD* thd, execplan::SCSEP& csep, cal_impl_if::cal_group_info& gi);
int cs_get_derived_plan(derived_handler* handler, THD* thd, execplan::SCSEP& csep, gp_walk_info& gwi);
int cs_get_select_plan(select_handler* handler, THD* thd, execplan::SCSEP& csep, gp_walk_info& gwi);
int cs_get_derived_plan(ha_columnstore_derived_handler* handler, THD* thd, execplan::SCSEP& csep, gp_walk_info& gwi);
int cs_get_select_plan(ha_columnstore_select_handler* handler, THD* thd, execplan::SCSEP& csep, gp_walk_info& gwi);
int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, execplan::SCSEP& csep, bool isUnion = false, bool isSelectHandlerTop = false, const std::vector<COND*>& condStack = std::vector<COND*>());
int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, execplan::SCSEP& csep, cal_group_info& gi, bool isUnion = false);
void setError(THD* thd, uint32_t errcode, const std::string errmsg, gp_walk_info* gwi);

View File

@ -19,234 +19,6 @@
#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->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:
@ -367,27 +139,3 @@ bool in_subselect_rewrite(SELECT_LEX *select_lex)
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);
}

View File

@ -22,9 +22,5 @@
bool in_subselect_rewrite(SELECT_LEX *select_lex);
void opt_flag_unset_PS(SELECT_LEX *select_lex);
COND *simplify_joins_mcs(JOIN *join, List<TABLE_LIST> *join_list,
COND *conds, bool top, bool in_sj);
uint build_bitmap_for_nested_joins_mcs(List<TABLE_LIST> *join_list,
uint first_unused);
#endif

View File

@ -21,7 +21,6 @@
void check_walk(const Item* item, void* arg);
void disable_indices_for_CEJ(THD *thd_)
{
TABLE_LIST* global_list;
@ -619,7 +618,7 @@ int ha_columnstore_derived_handler::init_scan()
{
DBUG_ENTER("ha_columnstore_derived_handler::init_scan");
mcs_handler_info mhi = mcs_handler_info(reinterpret_cast<void*>(this), DERIVED);
mcs_handler_info mhi(reinterpret_cast<void*>(this), DERIVED);
// this::table is the place for the result set
int rc = ha_mcs_impl_pushdown_init(&mhi, table);
@ -747,7 +746,7 @@ int ha_mcs_group_by_handler::end_scan()
* More details in server/sql/select_handler.h
* PARAMETERS:
* thd - THD pointer.
* sel - SELECT_LEX* that describes the query.
* select_lex - SELECT_LEX* that describes the query.
* RETURN:
* select_handler if possible
* NULL in other case
@ -806,7 +805,6 @@ create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex)
{
Query_arena *arena, backup;
arena = thd->activate_stmt_arena_if_needed(&backup);
disable_indices_for_CEJ(thd);
if (arena)
@ -822,25 +820,20 @@ create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex)
COND *conds = nullptr;
if (!unsupported_feature)
{
SELECT_LEX *sel= select_lex;
// Rewrite once for PS
// Refer to JOIN::optimize_inner() in sql/sql_select.cc
// for details on the optimizations performed in this block.
if (sel->first_cond_optimization)
if (select_lex->first_cond_optimization)
{
create_explain_query_if_not_exists(thd->lex, thd->mem_root);
arena = thd->activate_stmt_arena_if_needed(&backup);
sel->first_cond_optimization= false;
conds= simplify_joins_mcs(join, select_lex->join_list,
join->conds, true, false);
build_bitmap_for_nested_joins_mcs(select_lex->join_list, 0);
sel->where= conds;
select_lex->first_cond_optimization= false;
conds = join->conds;
select_lex->where = conds;
if (isPS)
{
sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
select_lex->prep_where = conds ? conds->copy_andor_structure(thd) : 0;
}
select_lex->update_used_tables();
@ -849,23 +842,21 @@ create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex)
thd->restore_active_arena(arena, &backup);
// Unset SL::first_cond_optimization
opt_flag_unset_PS(sel);
}
opt_flag_unset_PS(select_lex);
}
if (!unsupported_feature && conds)
{
#ifdef DEBUG_WALK_COND
if (conds)
{
conds->traverse_cond(cal_impl_if::debug_walk, NULL, Item::POSTFIX);
}
#endif
join->conds = conds;
}
}
// We shouldn't raise error now so set an error to raise it later in init_SH.
handler->rewrite_error = unsupported_feature;
// Return SH even if init fails b/c CS changed SELECT_LEX structures
// with simplify_joins_mcs()
// Return SH even if init fails
return handler;
}
@ -913,8 +904,7 @@ int ha_columnstore_select_handler::init_scan()
// Skip execution for EXPLAIN queries
if (!thd->lex->describe)
{
mcs_handler_info mhi= mcs_handler_info(
reinterpret_cast<void*>(this), SELECT);
mcs_handler_info mhi(reinterpret_cast<void*>(this), SELECT);
rc = ha_mcs_impl_pushdown_init(&mhi, this->table);
}
}

View File

@ -142,6 +142,10 @@ private:
public:
bool rewrite_error;
std::string err_msg;
// MCOL-4525 Store the original TABLE_LIST::outer_join value in a hash map.
// This will be used to restore to the original state later in case
// query execution fails using the select_handler.
cal_impl_if::TableOuterJoinMap tableOuterJoinMap;
ha_columnstore_select_handler(THD* thd_arg, SELECT_LEX* sel);
~ha_columnstore_select_handler();
int init_scan() override;

View File

@ -0,0 +1,223 @@
#
# MCOL-4665 Move outer join to inner join conversion into the engine.
#
DROP DATABASE IF EXISTS mcol4665;
CREATE DATABASE mcol4665;
USE mcol4665;
create table t1 (a int);
create table t2 (a int);
create table t3 (a int);
create table t4 (a int);
insert into t1 values (1), (2), (3), (4);
insert into t2 values (2), (3), (4);
insert into t3 values (3), (4);
insert into t4 values (4);
select * from t1 left join t2 on t1.a=t2.a order by 1,2;
a a
1 NULL
2 2
3 3
4 4
select * from t1 left join t2 on t1.a=t2.a where t2.a < 100 order by 1,2;
a a
2 2
3 3
4 4
select * from t1 left join t2 on t1.a=t2.a where t2.a is null order by 1,2;
a a
1 NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a order by 1,2,3;
a a a
1 NULL NULL
2 2 NULL
3 3 3
4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a where t2.a < 100 order by 1,2,3;
a a a
2 2 NULL
3 3 3
4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a where t2.a is null order by 1,2,3;
a a a
1 NULL NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a where t3.a < 100 order by 1,2,3;
a a a
3 3 3
4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a where t3.a is null order by 1,2,3;
a a a
1 NULL NULL
2 2 NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a order by 1,2,3;
a a a
1 NULL NULL
2 2 NULL
3 3 3
4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a where t2.a < 100 order by 1,2,3;
a a a
2 2 NULL
3 3 3
4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a where t2.a is null order by 1,2,3;
a a a
1 NULL NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a where t3.a < 100 order by 1,2,3;
a a a
3 3 3
4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a where t3.a is null order by 1,2,3;
a a a
1 NULL NULL
2 2 NULL
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a order by 1,2,3;
a a a
1 NULL NULL
2 NULL NULL
3 3 3
4 4 4
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a where t2.a < 100 order by 1,2,3;
a a a
3 3 3
4 4 4
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a where t3.a < 100 order by 1,2,3;
a a a
3 3 3
4 4 4
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a where t3.a is null order by 1,2,3;
a a a
1 NULL NULL
2 NULL NULL
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a order by 1,2,3;
a a a
1 NULL NULL
2 2 NULL
3 3 3
4 4 4
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a where t2.a < 100 order by 1,2,3;
a a a
2 2 NULL
3 3 3
4 4 4
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a where t2.a is null order by 1,2,3;
a a a
1 NULL NULL
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a where t3.a < 100 order by 1,2,3;
a a a
3 3 3
4 4 4
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a where t3.a is null order by 1,2,3;
a a a
1 NULL NULL
2 2 NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
3 3 3 NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t2.a < 100 order by 1,2,3,4;
a a a a
2 2 NULL NULL
3 3 3 NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t2.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t3.a < 100 order by 1,2,3,4;
a a a a
3 3 3 NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t3.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t4.a < 100 order by 1,2,3,4;
a a a a
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t4.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
3 3 3 NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
3 3 NULL NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t2.a < 100 order by 1,2,3,4;
a a a a
2 2 NULL NULL
3 3 NULL NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t2.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t3.a < 100 order by 1,2,3,4;
a a a a
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t4.a < 100 order by 1,2,3,4;
a a a a
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t4.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
3 3 NULL NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
3 3 3 NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t2.a < 100 order by 1,2,3,4;
a a a a
2 2 NULL NULL
3 3 3 NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t2.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t3.a < 100 order by 1,2,3,4;
a a a a
3 3 3 NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t3.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t4.a < 100 order by 1,2,3,4;
a a a a
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t4.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
3 3 3 NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
3 3 NULL NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t2.a < 100 order by 1,2,3,4;
a a a a
2 2 NULL NULL
3 3 NULL NULL
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t2.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t3.a < 100 order by 1,2,3,4;
a a a a
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t4.a < 100 order by 1,2,3,4;
a a a a
4 4 4 4
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t4.a is null order by 1,2,3,4;
a a a a
1 NULL NULL NULL
2 2 NULL NULL
3 3 NULL NULL
DROP DATABASE mcol4665;

View File

@ -0,0 +1,84 @@
--source ../include/have_columnstore.inc
--source ctype_cmp_combinations.inc
--source default_storage_engine_by_combination.inc
--echo #
--echo # MCOL-4665 Move outer join to inner join conversion into the engine.
--echo #
--disable_warnings
DROP DATABASE IF EXISTS mcol4665;
--enable_warnings
CREATE DATABASE mcol4665;
USE mcol4665;
create table t1 (a int);
create table t2 (a int);
create table t3 (a int);
create table t4 (a int);
insert into t1 values (1), (2), (3), (4);
insert into t2 values (2), (3), (4);
insert into t3 values (3), (4);
insert into t4 values (4);
select * from t1 left join t2 on t1.a=t2.a order by 1,2;
select * from t1 left join t2 on t1.a=t2.a where t2.a < 100 order by 1,2;
select * from t1 left join t2 on t1.a=t2.a where t2.a is null order by 1,2;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a where t2.a < 100 order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a where t2.a is null order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a where t3.a < 100 order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t1.a=t3.a where t3.a is null order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a where t2.a < 100 order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a where t2.a is null order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a where t3.a < 100 order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a where t3.a is null order by 1,2,3;
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a order by 1,2,3;
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a where t2.a < 100 order by 1,2,3;
# Below query is disabled until MCOL-4715 is fixed
# select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a where t2.a is null order by 1,2,3;
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a where t3.a < 100 order by 1,2,3;
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t3.a where t3.a is null order by 1,2,3;
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a order by 1,2,3;
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a where t2.a < 100 order by 1,2,3;
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a where t2.a is null order by 1,2,3;
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a where t3.a < 100 order by 1,2,3;
select * from t1 left join t2 left join t3 on t2.a=t3.a on t1.a=t2.a where t3.a is null order by 1,2,3;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t2.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t2.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t3.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t3.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t4.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t3.a where t4.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t2.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t2.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t3.a < 100 order by 1,2,3,4;
# Below query is disabled until MCOL-4715 is fixed
# select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t3.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t4.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t1.a=t4.a where t4.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t2.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t2.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t3.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t3.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t4.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t3.a where t4.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t2.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t2.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t3.a < 100 order by 1,2,3,4;
# Below query is disabled until MCOL-4715 is fixed
# select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t3.a is null order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t4.a < 100 order by 1,2,3,4;
select * from t1 left join t2 on t1.a=t2.a left join t3 left join t4 on t3.a=t4.a on t2.a=t4.a where t4.a is null order by 1,2,3,4;
DROP DATABASE mcol4665;