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 #1912 from tntnatbry/MCOL-4680-dev

MCOL-4680 FROM subquery containing nested joins returns an error.
This commit is contained in:
Roman Nozdrin
2021-05-06 13:52:36 +03:00
committed by GitHub
6 changed files with 273 additions and 272 deletions

View File

@ -134,6 +134,15 @@ public:
namespace cal_impl_if namespace cal_impl_if
{ {
CalpontSystemCatalog::TableAliasName makeTableAliasName(TABLE_LIST* table)
{
return make_aliasview(
(table->db.length ? table->db.str : ""),
(table->table_name.length ? table->table_name.str : ""),
(table->alias.length ? table->alias.str : ""),
getViewName(table), true, lower_case_table_names);
}
//@bug5228. need to escape backtick ` //@bug5228. need to escape backtick `
string escapeBackTick(const char* str) string escapeBackTick(const char* str)
{ {
@ -1239,245 +1248,122 @@ void debug_walk(const Item* item, void* arg)
} }
#endif #endif
void buildNestedTableOuterJoin(gp_walk_info& gwi, TABLE_LIST* table_ptr) void buildNestedJoinLeafTables(List<TABLE_LIST>& join_list,
std::set<execplan::CalpontSystemCatalog::TableAliasName>& leafTables)
{ {
TABLE_LIST* table; TABLE_LIST *table;
List_iterator<TABLE_LIST> li(table_ptr->nested_join->join_list); List_iterator<TABLE_LIST> li(join_list);
while ((table = li++)) while ((table = li++))
{ {
gwi.innerTables.clear();
if (table->outer_join)
{
CalpontSystemCatalog::TableAliasName ta = make_aliasview(
(table->db.length ? table->db.str : ""),
(table->table_name.length ? table->table_name.str : ""),
(table->alias.length ? table->alias.str : ""),
getViewName(table), true, lower_case_table_names);
gwi.innerTables.insert(ta);
}
if (table->nested_join) if (table->nested_join)
buildNestedJoinLeafTables(table->nested_join->join_list, leafTables);
else
{ {
TABLE_LIST* tab; CalpontSystemCatalog::TableAliasName tan = makeTableAliasName(table);
List_iterator<TABLE_LIST> li(table->nested_join->join_list); leafTables.insert(tan);
while ((tab = li++))
{
CalpontSystemCatalog::TableAliasName ta = make_aliasview(
(tab->db.length ? tab->db.str : ""),
(tab->table_name.length ? tab->table_name.str : ""),
(tab->alias.length ? tab->alias.str : ""),
getViewName(tab), true, lower_case_table_names);
gwi.innerTables.insert(ta);
}
}
if (table->on_expr)
{
Item_cond* expr = reinterpret_cast<Item_cond*>(table->on_expr);
#ifdef DEBUG_WALK_COND
expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif
expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
}
if (table->nested_join && &(table->nested_join->join_list))
{
buildNestedTableOuterJoin(gwi, table);
} }
} }
} }
uint32_t buildOuterJoin(gp_walk_info& gwi, SELECT_LEX& select_lex) uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
std::stack<execplan::ParseTree*>& outerJoinStack)
{ {
// check non-collapsed outer join TABLE_LIST *table;
// this set contains all processed embedded joins. duplicate joins are ignored List_iterator<TABLE_LIST> li(join_list);
set<TABLE_LIST*> embeddingSet;
TABLE_LIST* table_ptr = select_lex.get_table_list();
gp_walk_info gwi_outer = gwi;
gwi_outer.subQuery = NULL;
gwi_outer.hasSubSelect = false;
vector <Item_field*> tmpVec;
for (; table_ptr; table_ptr = table_ptr->next_local) while ((table = li++))
{ {
gwi_outer.innerTables.clear(); // Make sure we don't process the derived table nests again,
clearStacks(gwi_outer); // they were already handled by FromSubQuery::transform()
gwi_outer.subQuery = NULL; if (table->nested_join && !table->derived)
gwi_outer.hasSubSelect = false; buildJoin(gwi, table->nested_join->join_list, outerJoinStack);
// View is already processed in view::transform if (table->on_expr)
// @bug5319. view is sometimes treated as derived table and
// fromSub::transform does not build outer join filters.
if (!table_ptr->derived && table_ptr->view && !gwi.subQuery)
continue;
CalpontSystemCatalog:: TableAliasName tan = make_aliasview(
(table_ptr->db.length ? table_ptr->db.str : ""),
(table_ptr->table_name.length ? table_ptr->table_name.str : ""),
(table_ptr->alias.length ? table_ptr->alias.str : ""),
getViewName(table_ptr), true, lower_case_table_names);
if ((table_ptr->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT)) && table_ptr->on_expr)
{ {
// inner tables block Item_cond* expr = reinterpret_cast<Item_cond*>(table->on_expr);
Item_cond* expr = reinterpret_cast<Item_cond*>(table_ptr->on_expr);
gwi_outer.innerTables.insert(tan);
if (table_ptr->nested_join && &(table_ptr->nested_join->join_list)) if (table->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT))
{ {
TABLE_LIST* table; // inner tables block
List_iterator<TABLE_LIST> li(table_ptr->nested_join->join_list); gp_walk_info gwi_outer = gwi;
gwi_outer.subQuery = NULL;
gwi_outer.hasSubSelect = false;
gwi_outer.innerTables.clear();
clearStacks(gwi_outer);
while ((table = li++)) // recursively build the leaf tables for this nested join node
if (table->nested_join)
buildNestedJoinLeafTables(table->nested_join->join_list,
gwi_outer.innerTables);
else // this is a leaf table
{ {
CalpontSystemCatalog::TableAliasName ta = make_aliasview( CalpontSystemCatalog::TableAliasName tan = makeTableAliasName(table);
(table->db.length ? table->db.str : ""), gwi_outer.innerTables.insert(tan);
(table->table_name.length ? table->table_name.str : ""),
(table->alias.length ? table->alias.str : ""),
getViewName(table), true, lower_case_table_names);
gwi_outer.innerTables.insert(ta);
} }
}
#ifdef DEBUG_WALK_COND #ifdef DEBUG_WALK_COND
cerr << "inner tables: " << endl;
set<CalpontSystemCatalog::TableAliasName>::const_iterator it;
for (it = gwi_outer.innerTables.begin(); it != gwi_outer.innerTables.end(); ++it)
cerr << (*it) << " ";
cerr << endl;
if (table_ptr->alias.length) cerr << " outer table expression: " << endl;
cerr << table_ptr->alias.str; expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
else if (table_ptr->alias.length)
cerr << table_ptr->alias.str;
cerr << " outer table expression: " << endl;
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
#endif #endif
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
}
else if (table_ptr->nested_join && &(table_ptr->nested_join->join_list))
{
buildNestedTableOuterJoin(gwi_outer, table_ptr);
}
// this part is ambiguous. Not quite sure how MySQL's lay out the outer join filters in the structure
else if (table_ptr->embedding && table_ptr->embedding->outer_join && table_ptr->embedding->on_expr)
{
// all the tables in nested_join are inner tables.
TABLE_LIST* table;
List_iterator<TABLE_LIST> li(table_ptr->embedding->nested_join->join_list);
gwi_outer.innerTables.clear();
while ((table = li++)) expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
{
CalpontSystemCatalog:: TableAliasName ta = make_aliasview(
(table->db.length ? table->db.str : ""),
(table->table_name.length ? table->table_name.str : ""),
(table->alias.length ? table->alias.str : ""),
getViewName(table), true, lower_case_table_names);
gwi_outer.innerTables.insert(ta);
}
if (embeddingSet.find(table_ptr->embedding) != embeddingSet.end()) // Error out subquery in outer join on filter for now
continue; if (gwi_outer.hasSubSelect)
embeddingSet.insert(table_ptr->embedding);
Item_cond* expr = reinterpret_cast<Item_cond*>(table_ptr->embedding->on_expr);
#ifdef DEBUG_WALK_COND
cerr << "inner tables: " << endl;
set<CalpontSystemCatalog::TableAliasName>::const_iterator it;
for (it = gwi_outer.innerTables.begin(); it != gwi_outer.innerTables.end(); ++it)
cerr << (*it) << " ";
cerr << endl;
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
#endif
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
}
// *DRRTUY This part is to be removed in 1.4.3
#if 0
// @bug 2849
/*else if (table_ptr->embedding && table_ptr->embedding->nested_join)
{
// if this is dervied table process phase, mysql may have not developed the plan
// completely. Return and let it finish. It will come to rnd_init again.
if (table_ptr->embedding->is_natural_join && table_ptr->derived)
{
if (gwi.thd->derived_tables_processing)
{ {
// MCOL-2178 isUnion member only assigned, never used gwi.fatalParseError = true;
//MIGR::infinidb_vtable.isUnion = false; gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_OUTER_JOIN_SUBSELECT);
gwi.cs_vtable_is_update_with_derive = true; setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText);
return -1; return -1;
} }
}
if (embeddingSet.find(table_ptr->embedding) != embeddingSet.end()) // build outerjoinon filter
continue; ParseTree* filters = NULL, *ptp = NULL, *rhs = NULL;
gwi_outer.innerTables.clear(); while (!gwi_outer.ptWorkStack.empty())
gwi_outer.innerTables.insert(tan);
embeddingSet.insert(table_ptr->embedding);
List<TABLE_LIST>* inners = &(table_ptr->embedding->nested_join->join_list);
List_iterator_fast<TABLE_LIST> li(*inners);
TABLE_LIST* curr;
while ((curr = li++))
{
if (curr->on_expr)
{ {
if (!curr->outer_join) // only handle nested JOIN for now filters = gwi_outer.ptWorkStack.top();
{ gwi_outer.ptWorkStack.pop();
gwi_outer.innerTables.clear();
Item_cond* expr = reinterpret_cast<Item_cond*>(curr->on_expr);
#ifdef DEBUG_WALK_COND if (gwi_outer.ptWorkStack.empty())
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX); break;
#endif
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX); ptp = new ParseTree(new LogicOperator("and"));
} ptp->left(filters);
rhs = gwi_outer.ptWorkStack.top();
gwi_outer.ptWorkStack.pop();
ptp->right(rhs);
gwi_outer.ptWorkStack.push(ptp);
}
// should have only 1 pt left in stack.
if (filters)
{
SPTP on_sp(filters);
OuterJoinOnFilter* onFilter = new OuterJoinOnFilter(on_sp);
ParseTree* pt = new ParseTree(onFilter);
outerJoinStack.push(pt);
} }
} }
} */ else // inner join
{
expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
#ifdef DEBUG_WALK_COND
cerr << " inner join expression: " << endl;
expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif #endif
// Error out subquery in outer join on filter for now }
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
ParseTree* filters = NULL, *ptp = NULL, *lhs = NULL;
while (!gwi_outer.ptWorkStack.empty())
{
filters = gwi_outer.ptWorkStack.top();
gwi_outer.ptWorkStack.pop();
if (gwi_outer.ptWorkStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->right(filters);
lhs = gwi_outer.ptWorkStack.top();
gwi_outer.ptWorkStack.pop();
ptp->left(lhs);
gwi_outer.ptWorkStack.push(ptp);
}
// should have only 1 pt left in stack.
if (filters)
{
SPTP on_sp(filters);
OuterJoinOnFilter* onFilter = new OuterJoinOnFilter(on_sp);
ParseTree* pt = new ParseTree(onFilter);
gwi.ptWorkStack.push(pt);
} }
} }
embeddingSet.clear();
return 0; return 0;
} }
@ -6289,16 +6175,13 @@ void setExecutionParams(gp_walk_info &gwi, SCSEP &csep)
* FROM part of the query. * FROM part of the query.
* isUnion tells that CS processes FROM taken from UNION UNIT. * isUnion tells that CS processes FROM taken from UNION UNIT.
* The notion is described in MDB code. * The notion is described in MDB code.
* on_expr_list ON expressions used in OUTER JOINs. These are
* later used in processWhere()
* RETURNS * RETURNS
* error id as an int * error id as an int
***********************************************************/ ***********************************************************/
int processFrom(bool &isUnion, int processFrom(bool &isUnion,
SELECT_LEX &select_lex, SELECT_LEX &select_lex,
gp_walk_info &gwi, gp_walk_info &gwi,
SCSEP &csep, SCSEP &csep)
List<Item> &on_expr_list)
{ {
// populate table map and trigger syscolumn cache for all the tables (@bug 1637). // populate table map and trigger syscolumn cache for all the tables (@bug 1637).
// all tables on FROM list must have at least one col in colmap // all tables on FROM list must have at least one col in colmap
@ -6328,12 +6211,6 @@ int processFrom(bool &isUnion,
return ER_CHECK_NOT_IMPLEMENTED; return ER_CHECK_NOT_IMPLEMENTED;
} }
// Save on_expr to use it for WHERE processing
if (!(table_ptr->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT)) && table_ptr->on_expr)
{
on_expr_list.push_back(table_ptr->on_expr);
}
string viewName = getViewName(table_ptr); string viewName = getViewName(table_ptr);
if (lower_case_table_names) if (lower_case_table_names)
{ {
@ -6483,15 +6360,12 @@ int processFrom(bool &isUnion,
* DESCRIPTION: * DESCRIPTION:
* This function processes conditions from either JOIN->conds * This function processes conditions from either JOIN->conds
* or SELECT_LEX->where|prep_where * or SELECT_LEX->where|prep_where
* on_expr_list ON expressions used in OUTER JOINs. These are
* populated used in processFrom()
* RETURNS * RETURNS
* error id as an int * error id as an int
***********************************************************/ ***********************************************************/
int processWhere(SELECT_LEX &select_lex, int processWhere(SELECT_LEX &select_lex,
gp_walk_info &gwi, gp_walk_info &gwi,
SCSEP &csep, SCSEP &csep,
List<Item> &on_expr_list,
const std::vector<COND*>& condStack) const std::vector<COND*>& condStack)
{ {
JOIN* join = select_lex.join; JOIN* join = select_lex.join;
@ -6609,27 +6483,6 @@ 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());
} }
#ifdef DEBUG_WALK_COND
std::cerr << "------------------ ON_EXPR -----------------------" << endl;
#endif
// MCOL-3593 MDB now doesn't rewrite and/or consolidate ON and WHERE expressions
// and CS handles INNER ON expressions here.
if (!on_expr_list.is_empty())
{
List_iterator<Item> on_expr_it(on_expr_list);
Item_cond *on_expr = NULL;
while((on_expr = reinterpret_cast<Item_cond*>(on_expr_it++)))
{
on_expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
#ifdef DEBUG_WALK_COND
on_expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif
}
}
#ifdef DEBUG_WALK_COND
std::cerr << "-------------------------------------------------\n" << std::endl;
#endif
// 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
@ -6694,26 +6547,31 @@ int processWhere(SELECT_LEX &select_lex,
} }
#endif #endif
uint32_t failed = buildOuterJoin(gwi, select_lex); uint32_t failed = 0;
if (failed) return failed; // InfiniDB bug5764 requires outer joins to be appended to the
// end of the filter list. This causes outer join filters to
// have a higher join id than inner join filters.
// TODO MCOL-4680 Figure out why this is the case, and possibly
// eliminate this requirement.
std::stack<execplan::ParseTree*> outerJoinStack;
// @bug5764. build outer join for view, make sure outerjoin filter is appended if ((failed = buildJoin(gwi, select_lex.top_join_list, outerJoinStack)))
// to the end of the filter list.
for (uint i = 0; i < gwi.viewList.size(); i++)
{
failed = gwi.viewList[i]->processOuterJoin(gwi);
if (failed)
break;
}
if (failed != 0)
return failed; return failed;
if (gwi.subQuery)
{
for (uint i = 0; i < gwi.viewList.size(); i++)
{
if ((failed = gwi.viewList[i]->processJoin(gwi, outerJoinStack)))
return failed;
}
}
ParseTree* filters = NULL; ParseTree* filters = NULL;
ParseTree* outerJoinFilters = NULL;
ParseTree* ptp = NULL; ParseTree* ptp = NULL;
ParseTree* lhs = NULL; ParseTree* rhs = NULL;
// @bug 2932. for "select * from region where r_name" case. if icp not null and // @bug 2932. for "select * from region where r_name" case. if icp not null and
// ptWorkStack empty, the item is in rcWorkStack. // ptWorkStack empty, the item is in rcWorkStack.
@ -6734,13 +6592,45 @@ int processWhere(SELECT_LEX &select_lex,
break; break;
ptp = new ParseTree(new LogicOperator("and")); ptp = new ParseTree(new LogicOperator("and"));
ptp->right(filters); ptp->left(filters);
lhs = gwi.ptWorkStack.top(); rhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop(); gwi.ptWorkStack.pop();
ptp->left(lhs); ptp->right(rhs);
gwi.ptWorkStack.push(ptp); gwi.ptWorkStack.push(ptp);
} }
while (!outerJoinStack.empty())
{
outerJoinFilters = outerJoinStack.top();
outerJoinStack.pop();
if (outerJoinStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(outerJoinFilters);
rhs = outerJoinStack.top();
outerJoinStack.pop();
ptp->right(rhs);
outerJoinStack.push(ptp);
}
// Append outer join filters at the end of inner join filters.
// JLF_ExecPlanToJobList::walkTree processes ParseTree::left
// before ParseTree::right which is what we intend to do in the
// below.
if (filters && outerJoinFilters)
{
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
ptp->right(outerJoinFilters);
filters = ptp;
}
else if (outerJoinFilters)
{
filters = outerJoinFilters;
}
if (filters) if (filters)
{ {
csep->filters(filters); csep->filters(filters);
@ -6928,15 +6818,15 @@ int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex,
CalpontSelectExecutionPlan::SelectList derivedTbList; CalpontSelectExecutionPlan::SelectList derivedTbList;
// @bug 1796. Remember table order on the FROM list. // @bug 1796. Remember table order on the FROM list.
gwi.clauseType = FROM; gwi.clauseType = FROM;
List<Item> on_expr_list; if ((rc = processFrom(isUnion, select_lex, gwi, csep)))
if ((rc = processFrom(isUnion, select_lex, gwi, csep, on_expr_list)))
{ {
return rc; return rc;
} }
bool unionSel = (!isUnion && select_lex.master_unit()->is_unit_op()) ? true : false; bool unionSel = (!isUnion && select_lex.master_unit()->is_unit_op()) ? true : false;
gwi.clauseType = WHERE; gwi.clauseType = WHERE;
if ((rc = processWhere(select_lex, gwi, csep, on_expr_list, condStack))) if ((rc = processWhere(select_lex, gwi, csep, condStack)))
{ {
return rc; return rc;
} }
@ -8628,26 +8518,36 @@ int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_gro
SELECT_LEX tmp_select_lex; SELECT_LEX tmp_select_lex;
tmp_select_lex.table_list.first = gi.groupByTables; tmp_select_lex.table_list.first = gi.groupByTables;
uint32_t failed = buildOuterJoin(gwi, tmp_select_lex);
// InfiniDB bug5764 requires outer joins to be appended to the
// end of the filter list. This causes outer join filters to
// have a higher join id than inner join filters.
// TODO MCOL-4680 Figure out why this is the case, and possibly
// eliminate this requirement.
std::stack<execplan::ParseTree*> outerJoinStack;
uint32_t failed = buildJoin(gwi, tmp_select_lex.top_join_list, outerJoinStack);
if (failed) return failed; if (failed) return failed;
// @bug5764. build outer join for view, make sure outerjoin filter is appended if (gwi.subQuery)
// to the end of the filter list.
for (uint i = 0; i < gwi.viewList.size(); i++)
{ {
failed = gwi.viewList[i]->processOuterJoin(gwi); for (uint i = 0; i < gwi.viewList.size(); i++)
{
failed = gwi.viewList[i]->processJoin(gwi, outerJoinStack);
if (failed) if (failed)
break; break;
}
} }
if (failed != 0) if (failed != 0)
return failed; return failed;
ParseTree* filters = NULL; ParseTree* filters = NULL;
ParseTree* outerJoinFilters = NULL;
ParseTree* ptp = NULL; ParseTree* ptp = NULL;
ParseTree* lhs = NULL; ParseTree* rhs = NULL;
// @bug 2932. for "select * from region where r_name" case. if icp not null and // @bug 2932. for "select * from region where r_name" case. if icp not null and
// ptWorkStack empty, the item is in rcWorkStack. // ptWorkStack empty, the item is in rcWorkStack.
@ -8668,15 +8568,45 @@ int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_gro
break; break;
ptp = new ParseTree(new LogicOperator("and")); ptp = new ParseTree(new LogicOperator("and"));
//ptp->left(filters); ptp->left(filters);
ptp->right(filters); rhs = gwi.ptWorkStack.top();
lhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop(); gwi.ptWorkStack.pop();
//ptp->right(rhs); ptp->right(rhs);
ptp->left(lhs);
gwi.ptWorkStack.push(ptp); gwi.ptWorkStack.push(ptp);
} }
while (!outerJoinStack.empty())
{
outerJoinFilters = outerJoinStack.top();
outerJoinStack.pop();
if (outerJoinStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(outerJoinFilters);
rhs = outerJoinStack.top();
outerJoinStack.pop();
ptp->right(rhs);
outerJoinStack.push(ptp);
}
// Append outer join filters at the end of inner join filters.
// JLF_ExecPlanToJobList::walkTree processes ParseTree::left
// before ParseTree::right which is what we intend to do in the
// below.
if (filters && outerJoinFilters)
{
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
ptp->right(outerJoinFilters);
filters = ptp;
}
else if (outerJoinFilters)
{
filters = outerJoinFilters;
}
if (filters) if (filters)
{ {
csep->filters(filters); csep->filters(filters);

View File

@ -373,7 +373,7 @@ bool buildRowColumnFilter(gp_walk_info* gwip, execplan::RowColumn* rhs, execplan
bool buildPredicateItem(Item_func* ifp, gp_walk_info* gwip); bool buildPredicateItem(Item_func* ifp, gp_walk_info* gwip);
void collectAllCols(gp_walk_info& gwi, Item_field* ifp); void collectAllCols(gp_walk_info& gwi, Item_field* ifp);
void buildSubselectFunc(Item_func* ifp, gp_walk_info* gwip); void buildSubselectFunc(Item_func* ifp, gp_walk_info* gwip);
uint32_t buildOuterJoin(gp_walk_info& gwi, SELECT_LEX& select_lex); uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list, std::stack<execplan::ParseTree*>& outerJoinStack);
std::string getViewName(TABLE_LIST* table_ptr); std::string getViewName(TABLE_LIST* table_ptr);
bool buildConstPredicate(Item_func* ifp, execplan::ReturnedColumn* rhs, gp_walk_info* gwip); bool buildConstPredicate(Item_func* ifp, execplan::ReturnedColumn* rhs, gp_walk_info* gwip);
execplan::CalpontSystemCatalog::ColType fieldType_MysqlToIDB (const Field* field); execplan::CalpontSystemCatalog::ColType fieldType_MysqlToIDB (const Field* field);

View File

@ -44,7 +44,8 @@ using namespace execplan;
namespace cal_impl_if namespace cal_impl_if
{ {
extern uint32_t buildOuterJoin(gp_walk_info& gwi, SELECT_LEX& select_lex); extern uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
std::stack<execplan::ParseTree*>& outerJoinStack);
extern string getViewName(TABLE_LIST* table_ptr); extern string getViewName(TABLE_LIST* table_ptr);
CalpontSystemCatalog::TableAliasName& View::viewName() CalpontSystemCatalog::TableAliasName& View::viewName()
@ -182,9 +183,9 @@ void View::transform()
} }
} }
uint32_t View::processOuterJoin(gp_walk_info& gwi) uint32_t View::processJoin(gp_walk_info& gwi, std::stack<execplan::ParseTree*>& outerJoinStack)
{ {
return buildOuterJoin(gwi, fSelect); return buildJoin(gwi, fSelect.top_join_list, outerJoinStack);
} }
} }

View File

@ -53,7 +53,7 @@ public:
parent select. parent select.
*/ */
void transform(); void transform();
uint32_t processOuterJoin(gp_walk_info& gwi); uint32_t processJoin(gp_walk_info& gwi, std::stack<execplan::ParseTree*>&);
private: private:
SELECT_LEX fSelect; SELECT_LEX fSelect;

View File

@ -0,0 +1,29 @@
#
# FROM subquery containing nested joins returns an error
#
DROP DATABASE IF EXISTS mcol4680;
CREATE DATABASE mcol4680;
USE mcol4680;
create table t1 (a int);
insert into t1 values (1), (2), (3);
create table t2 (a int);
insert into t2 values (2);
create table t3 (a int);
create table t4 (a int);
create table t5 (a int);
select * from
(
select t1.a as col1, t2.a as col2 from
t1 left join
(
(t2 left join t3 on t2.a=t3.a) left join
(t4 left join t5 on t4.a=t5.a)
on t2.a=t4.a
)
on t1.a=t2.a
) h order by col1;
col1 col2
1 NULL
2 2
3 NULL
DROP DATABASE mcol4680;

View File

@ -0,0 +1,41 @@
--source ../include/have_columnstore.inc
--source ctype_cmp_combinations.inc
--source default_storage_engine_by_combination.inc
--echo #
--echo # FROM subquery containing nested joins returns an error
--echo #
--disable_warnings
DROP DATABASE IF EXISTS mcol4680;
--enable_warnings
CREATE DATABASE mcol4680;
USE mcol4680;
create table t1 (a int);
insert into t1 values (1), (2), (3);
create table t2 (a int);
insert into t2 values (2);
create table t3 (a int);
create table t4 (a int);
create table t5 (a int);
select * from
(
select t1.a as col1, t2.a as col2 from
t1 left join
(
(t2 left join t3 on t2.a=t3.a) left join
(t4 left join t5 on t4.a=t5.a)
on t2.a=t4.a
)
on t1.a=t2.a
) h order by col1;
DROP DATABASE mcol4680;