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

Merge pull request #1935 from tntnatbry/MCOL-4665

MCOL-4665 Move outer join to inner join conversion into the engine.
This commit is contained in:
Roman Nozdrin
2021-06-03 16:05:58 +03:00
committed by GitHub
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_impl_if.h"
#include "ha_mcs_sysvars.h" #include "ha_mcs_sysvars.h"
#include "ha_subquery.h" #include "ha_subquery.h"
#include "ha_mcs_pushdown.h"
using namespace cal_impl_if; using namespace cal_impl_if;
#include "calpontselectexecutionplan.h" #include "calpontselectexecutionplan.h"
@ -136,6 +137,142 @@ public:
namespace cal_impl_if 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) CalpontSystemCatalog::TableAliasName makeTableAliasName(TABLE_LIST* table)
{ {
return make_aliasview( 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) if (table->nested_join && !table->derived)
buildJoin(gwi, table->nested_join->join_list, outerJoinStack); buildJoin(gwi, table->nested_join->join_list, outerJoinStack);
if (table->on_expr) std::vector<COND*> tableOnExprList;
{
Item_cond* expr = reinterpret_cast<Item_cond*>(table->on_expr);
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)) if (table->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT))
{ {
// inner tables block // inner tables block
@ -1312,18 +1463,23 @@ uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
cerr << endl; cerr << endl;
cerr << " outer table expression: " << endl; cerr << " outer table expression: " << endl;
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
for (Item* expr : tableOnExprList)
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
#endif #endif
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX); for (Item* expr : tableOnExprList)
// Error out subquery in outer join on filter for now
if (gwi_outer.hasSubSelect)
{ {
gwi.fatalParseError = true; expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_OUTER_JOIN_SUBSELECT);
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText); // Error out subquery in outer join on filter for now
return -1; if (gwi_outer.hasSubSelect)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_OUTER_JOIN_SUBSELECT);
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText);
return -1;
}
} }
// build outerjoinon filter // build outerjoinon filter
@ -1356,11 +1512,15 @@ uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
} }
else // inner join else // inner join
{ {
expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX); for (Item* expr : tableOnExprList)
{
expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
}
#ifdef DEBUG_WALK_COND #ifdef DEBUG_WALK_COND
cerr << " inner join expression: " << endl; cerr << " inner join expression: " << endl;
expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX); for (Item* expr : tableOnExprList)
expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif #endif
} }
} }
@ -2667,6 +2827,22 @@ void setError(THD* thd, uint32_t errcode, string errmsg, gp_walk_info& gwi)
clearStacks(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) const string bestTableName(const Item_field* ifp)
{ {
idbassert(ifp); idbassert(ifp);
@ -6364,7 +6540,6 @@ int processFrom(bool &isUnion,
csep->distinctUnionNum(distUnionNum); csep->distinctUnionNum(distUnionNum);
} }
return 0; return 0;
} }
@ -6382,24 +6557,24 @@ int processWhere(SELECT_LEX &select_lex,
const std::vector<COND*>& condStack) const std::vector<COND*>& condStack)
{ {
JOIN* join = select_lex.join; JOIN* join = select_lex.join;
Item_cond* icp = 0; Item* icp = 0;
bool isUpdateDelete = false; bool isUpdateDelete = false;
// Flag to indicate if this is a prepared statement // Flag to indicate if this is a prepared statement
bool isPS = gwi.thd->stmt_arena && gwi.thd->stmt_arena->is_stmt_execute(); bool isPS = gwi.thd->stmt_arena && gwi.thd->stmt_arena->is_stmt_execute();
if (join != 0 && !isPS) if (join != 0 && !isPS)
icp = reinterpret_cast<Item_cond*>(join->conds); icp = join->conds;
else if (isPS && select_lex.prep_where) 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 icp is null, try to find the where clause other where
if (!join && gwi.thd->lex->derived_tables) if (!join && gwi.thd->lex->derived_tables)
{ {
if (select_lex.prep_where) if (select_lex.prep_where)
icp = (Item_cond*)(select_lex.prep_where); icp = select_lex.prep_where;
else if (select_lex.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 ) || else if (!join && ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) ||
((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) ||
@ -6429,20 +6604,7 @@ int processWhere(SELECT_LEX &select_lex,
if (gwi.fatalParseError) if (gwi.fatalParseError)
{ {
// if this is dervied table process phase, mysql may have not developed the plan return setErrorAndReturn(gwi);
// 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;
} }
} }
else if (isUpdateDelete) else if (isUpdateDelete)
@ -6460,33 +6622,19 @@ int processWhere(SELECT_LEX &select_lex,
if (gwi.fatalParseError) if (gwi.fatalParseError)
{ {
if (gwi.thd->derived_tables_processing) return setErrorAndReturn(gwi);
{
gwi.cs_vtable_is_update_with_derive = true;
return -1;
}
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
} }
} }
} }
// if condStack is empty(), check the select_lex for where conditions // if condStack is empty(), check the select_lex for where conditions
// as a last resort // 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); icp->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
if (gwi.fatalParseError) if (gwi.fatalParseError)
{ {
if (gwi.thd->derived_tables_processing) return setErrorAndReturn(gwi);
{
gwi.cs_vtable_is_update_with_derive = true;
return -1;
}
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi);
return ER_INTERNAL_ERROR;
} }
} }
} }
@ -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()); (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 // ZZ - the followinig debug shows the structure of nested outer join. should
// use a recursive function. // use a recursive function.
#ifdef OUTER_JOIN_DEBUG #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; 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); int status = getSelectPlan(gwi, select_lex, csep, false);
if (status > 0) if (status > 0)
@ -8372,9 +8533,20 @@ int cs_get_derived_plan(derived_handler* handler, THD* thd, SCSEP& csep, gp_walk
return 0; 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); int status = getSelectPlan(gwi, select_lex, csep, false, true);
if (status > 0) 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; sm::cpsm_conhdl_t* hndl;
SCSEP csep; SCSEP csep;
// Declare handlers ptrs in this scope for future use. // Declare handlers ptrs in this scope for future use.
select_handler* sh = nullptr; ha_columnstore_select_handler* sh = nullptr;
derived_handler* dh = nullptr; ha_columnstore_derived_handler* dh = nullptr;
// update traceFlags according to the autoswitch state. // update traceFlags according to the autoswitch state.
ci->traceFlags = (ci->traceFlags | CalpontSelectExecutionPlan::TRACE_TUPLE_OFF)^ 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; int status = 42;
if (handler_info->hndl_type == mcs_handler_types_t::SELECT) 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); status = cs_get_select_plan(sh, thd, csep, gwi);
} }
else if (handler_info->hndl_type == DERIVED) 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); status = cs_get_derived_plan(dh, thd, csep, gwi);
} }

View File

@ -34,6 +34,8 @@
#include "idb_mysql.h" #include "idb_mysql.h"
struct st_ha_create_information; struct st_ha_create_information;
class ha_columnstore_select_handler;
class ha_columnstore_derived_handler;
#include "configcpp.h" #include "configcpp.h"
#include "idberrorinfo.h" #include "idberrorinfo.h"
@ -89,7 +91,8 @@ enum ClauseType
typedef std::vector<JoinInfo> JoinInfoVec; typedef std::vector<JoinInfo> JoinInfoVec;
typedef std::map<execplan::CalpontSystemCatalog::TableAliasName, std::pair<int, TABLE_LIST*> > TableMap; 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 struct gp_walk_info
{ {
@ -164,6 +167,10 @@ struct gp_walk_info
execplan::ReturnedColumn* inSubQueryLHS; execplan::ReturnedColumn* inSubQueryLHS;
Item* inSubQueryLHSItem; Item* inSubQueryLHSItem;
// The below 2 fields are required for MCOL-4525.
TableOnExprList tableOnExprList;
std::vector<COND*> condList;
gp_walk_info() : sessionid(0), gp_walk_info() : sessionid(0),
fatalParseError(false), fatalParseError(false),
condPush(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_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_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 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_derived_plan(ha_columnstore_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_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 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); 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); 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" #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*/ /*@brief in_subselect_rewrite_walk - Rewrites Item_in_subselect*/
/************************************************************ /************************************************************
* DESCRIPTION: * DESCRIPTION:
@ -367,27 +139,3 @@ bool in_subselect_rewrite(SELECT_LEX *select_lex)
return result; 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); bool in_subselect_rewrite(SELECT_LEX *select_lex);
void opt_flag_unset_PS(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 #endif

View File

@ -21,7 +21,6 @@
void check_walk(const Item* item, void* arg); void check_walk(const Item* item, void* arg);
void disable_indices_for_CEJ(THD *thd_) void disable_indices_for_CEJ(THD *thd_)
{ {
TABLE_LIST* global_list; TABLE_LIST* global_list;
@ -30,7 +29,7 @@ void disable_indices_for_CEJ(THD *thd_)
// MCOL-652 - doing this with derived tables can cause bad things to happen // MCOL-652 - doing this with derived tables can cause bad things to happen
if (!global_list->derived) if (!global_list->derived)
{ {
global_list->index_hints= new (thd_->mem_root) List<Index_hint>(); global_list->index_hints = new (thd_->mem_root) List<Index_hint>();
global_list->index_hints->push_front(new (thd_->mem_root) global_list->index_hints->push_front(new (thd_->mem_root)
Index_hint(INDEX_HINT_USE, Index_hint(INDEX_HINT_USE,
@ -84,8 +83,8 @@ void find_tables(const Item* item, void* arg)
{ {
if (typeid(*item) == typeid(Item_field)) if (typeid(*item) == typeid(Item_field))
{ {
Item_field *ifp= (Item_field*)item; Item_field *ifp = (Item_field*)item;
List<TABLE> *tables_list= (List<TABLE>*)arg; List<TABLE> *tables_list = (List<TABLE>*)arg;
tables_list->push_back(ifp->field->table); tables_list->push_back(ifp->field->table);
} }
} }
@ -109,8 +108,8 @@ bool is_joinkeys_predicate(const Item_func *ifp)
if (ifp->arguments()[0]->type() == Item::FIELD_ITEM && if (ifp->arguments()[0]->type() == Item::FIELD_ITEM &&
ifp->arguments()[1]->type() == Item::FIELD_ITEM) ifp->arguments()[1]->type() == Item::FIELD_ITEM)
{ {
Item_field* left= reinterpret_cast<Item_field*>(ifp->arguments()[0]); Item_field* left = reinterpret_cast<Item_field*>(ifp->arguments()[0]);
Item_field* right= reinterpret_cast<Item_field*>(ifp->arguments()[1]); Item_field* right = reinterpret_cast<Item_field*>(ifp->arguments()[1]);
// If MDB crashes here with non-fixed Item_field and field == NULL // If MDB crashes here with non-fixed Item_field and field == NULL
// there must be a check over on_expr for a different SELECT_LEX. // there must be a check over on_expr for a different SELECT_LEX.
@ -123,8 +122,8 @@ bool is_joinkeys_predicate(const Item_func *ifp)
else else
{ {
List<TABLE>llt; List<TABLE>rlt; List<TABLE>llt; List<TABLE>rlt;
Item *left= ifp->arguments()[0]; Item *left = ifp->arguments()[0];
Item *right= ifp->arguments()[1]; Item *right = ifp->arguments()[1];
// Search for tables inside left and right expressions // Search for tables inside left and right expressions
// and compare them // and compare them
left->traverse_cond(find_tables, (void*)&llt, Item::POSTFIX); left->traverse_cond(find_tables, (void*)&llt, Item::POSTFIX);
@ -133,7 +132,7 @@ bool is_joinkeys_predicate(const Item_func *ifp)
// the idea is useless. // the idea is useless.
if (llt.elements && rlt.elements && (llt.elem(0) != rlt.elem(0))) if (llt.elements && rlt.elements && (llt.elem(0) != rlt.elem(0)))
{ {
result= true; result = true;
} }
} }
} }
@ -151,7 +150,7 @@ bool is_joinkeys_predicate(const Item_func *ifp)
***********************************************************/ ***********************************************************/
void find_nonequi_join(const Item* item, void *arg) void find_nonequi_join(const Item* item, void *arg)
{ {
bool *unsupported_feature = reinterpret_cast<bool*>(arg); bool *unsupported_feature = reinterpret_cast<bool*>(arg);
if ( *unsupported_feature ) if ( *unsupported_feature )
return; return;
@ -184,7 +183,7 @@ void find_nonequi_join(const Item* item, void *arg)
***********************************************************/ ***********************************************************/
void find_join(const Item* item, void* arg) void find_join(const Item* item, void* arg)
{ {
bool *unsupported_feature = reinterpret_cast<bool*>(arg); bool *unsupported_feature = reinterpret_cast<bool*>(arg);
if ( *unsupported_feature ) if ( *unsupported_feature )
return; return;
@ -219,10 +218,10 @@ void save_join_predicates(const Item* item, void* arg)
{ {
if (item->type() == Item::FUNC_ITEM) if (item->type() == Item::FUNC_ITEM)
{ {
const Item_func* ifp= reinterpret_cast<const Item_func*>(item); const Item_func* ifp = reinterpret_cast<const Item_func*>(item);
if (is_joinkeys_predicate(ifp)) if (is_joinkeys_predicate(ifp))
{ {
List<Item> *join_preds_list= (List<Item>*)arg; List<Item> *join_preds_list = (List<Item>*)arg;
join_preds_list->push_back(const_cast<Item*>(item)); join_preds_list->push_back(const_cast<Item*>(item));
} }
} }
@ -241,7 +240,7 @@ void save_join_predicates(const Item* item, void* arg)
***********************************************************/ ***********************************************************/
void check_walk(const Item* item, void* arg) void check_walk(const Item* item, void* arg)
{ {
bool *unsupported_feature = reinterpret_cast<bool*>(arg); bool *unsupported_feature = reinterpret_cast<bool*>(arg);
if ( *unsupported_feature ) if ( *unsupported_feature )
return; return;
@ -307,7 +306,7 @@ void check_walk(const Item* item, void* arg)
***********************************************************/ ***********************************************************/
void check_user_var_func(const Item* item, void* arg) void check_user_var_func(const Item* item, void* arg)
{ {
bool* unsupported_feature = reinterpret_cast<bool*>(arg); bool* unsupported_feature = reinterpret_cast<bool*>(arg);
if (*unsupported_feature) if (*unsupported_feature)
return; return;
@ -509,8 +508,8 @@ create_columnstore_derived_handler(THD* thd, TABLE_LIST *table_ptr)
if (thd->stmt_arena && thd->stmt_arena->is_stmt_execute()) if (thd->stmt_arena && thd->stmt_arena->is_stmt_execute())
return handler; return handler;
SELECT_LEX_UNIT *unit= table_ptr->derived; SELECT_LEX_UNIT *unit = table_ptr->derived;
SELECT_LEX *sl= unit->first_select(); SELECT_LEX *sl = unit->first_select();
bool unsupported_feature = false; bool unsupported_feature = false;
@ -523,7 +522,7 @@ create_columnstore_derived_handler(THD* thd, TABLE_LIST *table_ptr)
} }
// JOIN expression from WHERE, ON expressions // JOIN expression from WHERE, ON expressions
JOIN* join= sl->join; JOIN* join = sl->join;
//TODO DRRTUY Make a proper tree traverse //TODO DRRTUY Make a proper tree traverse
//To search for CROSS JOIN-s we use tree invariant //To search for CROSS JOIN-s we use tree invariant
//G(V,E) where [V] = [E]+1 //G(V,E) where [V] = [E]+1
@ -533,7 +532,7 @@ create_columnstore_derived_handler(THD* thd, TABLE_LIST *table_ptr)
{ {
if (tl->where) if (tl->where)
{ {
Item_cond* where_icp= reinterpret_cast<Item_cond*>(tl->where); Item_cond* where_icp = reinterpret_cast<Item_cond*>(tl->where);
where_icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX); where_icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
where_icp->traverse_cond(save_join_predicates, &join_preds_list, Item::POSTFIX); where_icp->traverse_cond(save_join_predicates, &join_preds_list, Item::POSTFIX);
} }
@ -542,7 +541,7 @@ create_columnstore_derived_handler(THD* thd, TABLE_LIST *table_ptr)
// TABLE_LIST in FROM until CS meets unsupported feature // TABLE_LIST in FROM until CS meets unsupported feature
if (tl->on_expr) if (tl->on_expr)
{ {
Item_cond* on_icp= reinterpret_cast<Item_cond*>(tl->on_expr); Item_cond* on_icp = reinterpret_cast<Item_cond*>(tl->on_expr);
on_icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX); on_icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
on_icp->traverse_cond(save_join_predicates, &join_preds_list, Item::POSTFIX); on_icp->traverse_cond(save_join_predicates, &join_preds_list, Item::POSTFIX);
} }
@ -559,7 +558,7 @@ create_columnstore_derived_handler(THD* thd, TABLE_LIST *table_ptr)
if (!unsupported_feature && !join_preds_list.elements if (!unsupported_feature && !join_preds_list.elements
&& join && join->conds) && join && join->conds)
{ {
Item_cond* conds= reinterpret_cast<Item_cond*>(join->conds); Item_cond* conds = reinterpret_cast<Item_cond*>(join->conds);
conds->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX); conds->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
conds->traverse_cond(save_join_predicates, &join_preds_list, Item::POSTFIX); conds->traverse_cond(save_join_predicates, &join_preds_list, Item::POSTFIX);
} }
@ -569,18 +568,18 @@ create_columnstore_derived_handler(THD* thd, TABLE_LIST *table_ptr)
if (!unsupported_feature && join if (!unsupported_feature && join
&& join->table_count >= 2 && !join_preds_list.elements) && join->table_count >= 2 && !join_preds_list.elements)
{ {
unsupported_feature= true; unsupported_feature = true;
} }
// CROSS JOIN with not enough JOIN predicates // CROSS JOIN with not enough JOIN predicates
if(!unsupported_feature && join if(!unsupported_feature && join
&& join_preds_list.elements < join->table_count-1) && join_preds_list.elements < join->table_count-1)
{ {
unsupported_feature= true; unsupported_feature = true;
} }
if ( !unsupported_feature ) if (!unsupported_feature)
handler= new ha_columnstore_derived_handler(thd, table_ptr); handler = new ha_columnstore_derived_handler(thd, table_ptr);
return handler; return handler;
} }
@ -619,7 +618,7 @@ int ha_columnstore_derived_handler::init_scan()
{ {
DBUG_ENTER("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 // this::table is the place for the result set
int rc = ha_mcs_impl_pushdown_init(&mhi, table); 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 * More details in server/sql/select_handler.h
* PARAMETERS: * PARAMETERS:
* thd - THD pointer. * thd - THD pointer.
* sel - SELECT_LEX* that describes the query. * select_lex - SELECT_LEX* that describes the query.
* RETURN: * RETURN:
* select_handler if possible * select_handler if possible
* NULL in other case * NULL in other case
@ -800,13 +799,12 @@ create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex)
// We apply dedicated rewrites from MDB here so MDB's data structures // We apply dedicated rewrites from MDB here so MDB's data structures
// becomes dirty and CS has to raise an error in case of any problem // becomes dirty and CS has to raise an error in case of any problem
// or unsupported feature. // or unsupported feature.
handler= new ha_columnstore_select_handler(thd, select_lex); handler = new ha_columnstore_select_handler(thd, select_lex);
JOIN *join= select_lex->join; JOIN *join = select_lex->join;
bool unsupported_feature = false; bool unsupported_feature = false;
{ {
Query_arena *arena, backup; Query_arena *arena, backup;
arena= thd->activate_stmt_arena_if_needed(&backup); arena = thd->activate_stmt_arena_if_needed(&backup);
disable_indices_for_CEJ(thd); disable_indices_for_CEJ(thd);
if (arena) if (arena)
@ -822,25 +820,20 @@ create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex)
COND *conds = nullptr; COND *conds = nullptr;
if (!unsupported_feature) if (!unsupported_feature)
{ {
SELECT_LEX *sel= select_lex;
// Rewrite once for PS // Rewrite once for PS
// Refer to JOIN::optimize_inner() in sql/sql_select.cc // Refer to JOIN::optimize_inner() in sql/sql_select.cc
// for details on the optimizations performed in this block. // 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); create_explain_query_if_not_exists(thd->lex, thd->mem_root);
arena= thd->activate_stmt_arena_if_needed(&backup); arena = thd->activate_stmt_arena_if_needed(&backup);
sel->first_cond_optimization= false; select_lex->first_cond_optimization= false;
conds = join->conds;
conds= simplify_joins_mcs(join, select_lex->join_list, select_lex->where = conds;
join->conds, true, false);
build_bitmap_for_nested_joins_mcs(select_lex->join_list, 0);
sel->where= conds;
if (isPS) 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(); 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); thd->restore_active_arena(arena, &backup);
// Unset SL::first_cond_optimization // Unset SL::first_cond_optimization
opt_flag_unset_PS(sel); opt_flag_unset_PS(select_lex);
} }
}
if (!unsupported_feature && conds)
{
#ifdef DEBUG_WALK_COND #ifdef DEBUG_WALK_COND
conds->traverse_cond(cal_impl_if::debug_walk, NULL, Item::POSTFIX); if (conds)
{
conds->traverse_cond(cal_impl_if::debug_walk, NULL, Item::POSTFIX);
}
#endif #endif
join->conds = conds;
} }
} }
// We shouldn't raise error now so set an error to raise it later in init_SH. // We shouldn't raise error now so set an error to raise it later in init_SH.
handler->rewrite_error= unsupported_feature; handler->rewrite_error = unsupported_feature;
// Return SH even if init fails b/c CS changed SELECT_LEX structures // Return SH even if init fails
// with simplify_joins_mcs()
return handler; return handler;
} }
@ -880,8 +871,8 @@ ha_columnstore_select_handler::ha_columnstore_select_handler(THD *thd,
SELECT_LEX* select_lex) SELECT_LEX* select_lex)
: select_handler(thd, mcs_hton) : select_handler(thd, mcs_hton)
{ {
select= select_lex; select = select_lex;
rewrite_error= false; rewrite_error = false;
} }
/*********************************************************** /***********************************************************
@ -913,16 +904,15 @@ int ha_columnstore_select_handler::init_scan()
// Skip execution for EXPLAIN queries // Skip execution for EXPLAIN queries
if (!thd->lex->describe) if (!thd->lex->describe)
{ {
mcs_handler_info mhi= mcs_handler_info( mcs_handler_info mhi(reinterpret_cast<void*>(this), SELECT);
reinterpret_cast<void*>(this), SELECT); rc = ha_mcs_impl_pushdown_init(&mhi, this->table);
rc= ha_mcs_impl_pushdown_init(&mhi, this->table);
} }
} }
else else
{ {
my_printf_error(ER_INTERNAL_ERROR, "%s", MYF(0), err_msg.c_str()); my_printf_error(ER_INTERNAL_ERROR, "%s", MYF(0), err_msg.c_str());
sql_print_error("%s", err_msg.c_str()); sql_print_error("%s", err_msg.c_str());
rc= ER_INTERNAL_ERROR; rc = ER_INTERNAL_ERROR;
} }
DBUG_RETURN(rc); DBUG_RETURN(rc);
@ -942,7 +932,7 @@ int ha_columnstore_select_handler::next_row()
{ {
DBUG_ENTER("ha_columnstore_select_handler::next_row"); DBUG_ENTER("ha_columnstore_select_handler::next_row");
int rc= ha_mcs_impl_select_next(table->record[0], table); int rc = ha_mcs_impl_select_next(table->record[0], table);
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }

View File

@ -142,6 +142,10 @@ private:
public: public:
bool rewrite_error; bool rewrite_error;
std::string err_msg; 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(THD* thd_arg, SELECT_LEX* sel);
~ha_columnstore_select_handler(); ~ha_columnstore_select_handler();
int init_scan() override; 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;