You've already forked mariadb-columnstore-engine
mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-07-29 08:21:15 +03:00
MCOL-4680 FROM subquery containing nested joins returns an error.
Main theme of the patch is to fix joins processing in the plugin code. We now use SELECT_LEX::top_join_list and process the nested joins recursively, instead of SELECT_LEX::table_list struct which we earlier used to build the join filters. The earlier approach did not process certain nested join ON expressions, causing certain queries to incorrectly error out such as that described in MCOL-4680. In addition, some legacy code is also removed.
This commit is contained in:
@ -134,6 +134,15 @@ public:
|
||||
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 `
|
||||
string escapeBackTick(const char* str)
|
||||
{
|
||||
@ -1239,245 +1248,122 @@ void debug_walk(const Item* item, void* arg)
|
||||
}
|
||||
#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;
|
||||
List_iterator<TABLE_LIST> li(table_ptr->nested_join->join_list);
|
||||
TABLE_LIST *table;
|
||||
List_iterator<TABLE_LIST> li(join_list);
|
||||
|
||||
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)
|
||||
buildNestedJoinLeafTables(table->nested_join->join_list, leafTables);
|
||||
else
|
||||
{
|
||||
TABLE_LIST* tab;
|
||||
List_iterator<TABLE_LIST> li(table->nested_join->join_list);
|
||||
|
||||
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);
|
||||
CalpontSystemCatalog::TableAliasName tan = makeTableAliasName(table);
|
||||
leafTables.insert(tan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// this set contains all processed embedded joins. duplicate joins are ignored
|
||||
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;
|
||||
TABLE_LIST *table;
|
||||
List_iterator<TABLE_LIST> li(join_list);
|
||||
|
||||
for (; table_ptr; table_ptr = table_ptr->next_local)
|
||||
while ((table = li++))
|
||||
{
|
||||
gwi_outer.innerTables.clear();
|
||||
clearStacks(gwi_outer);
|
||||
gwi_outer.subQuery = NULL;
|
||||
gwi_outer.hasSubSelect = false;
|
||||
// Make sure we don't process the derived table nests again,
|
||||
// they were already handled by FromSubQuery::transform()
|
||||
if (table->nested_join && !table->derived)
|
||||
buildJoin(gwi, table->nested_join->join_list, outerJoinStack);
|
||||
|
||||
// View is already processed in view::transform
|
||||
// @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)
|
||||
if (table->on_expr)
|
||||
{
|
||||
// inner tables block
|
||||
Item_cond* expr = reinterpret_cast<Item_cond*>(table_ptr->on_expr);
|
||||
gwi_outer.innerTables.insert(tan);
|
||||
Item_cond* expr = reinterpret_cast<Item_cond*>(table->on_expr);
|
||||
|
||||
if (table_ptr->nested_join && &(table_ptr->nested_join->join_list))
|
||||
if (table->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT))
|
||||
{
|
||||
TABLE_LIST* table;
|
||||
List_iterator<TABLE_LIST> li(table_ptr->nested_join->join_list);
|
||||
// inner tables block
|
||||
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(
|
||||
(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);
|
||||
CalpontSystemCatalog::TableAliasName tan = makeTableAliasName(table);
|
||||
gwi_outer.innerTables.insert(tan);
|
||||
}
|
||||
}
|
||||
|
||||
#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 << table_ptr->alias.str;
|
||||
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);
|
||||
cerr << " outer table expression: " << endl;
|
||||
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
|
||||
#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++))
|
||||
{
|
||||
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);
|
||||
}
|
||||
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
|
||||
|
||||
if (embeddingSet.find(table_ptr->embedding) != embeddingSet.end())
|
||||
continue;
|
||||
|
||||
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)
|
||||
// Error out subquery in outer join on filter for now
|
||||
if (gwi_outer.hasSubSelect)
|
||||
{
|
||||
// MCOL-2178 isUnion member only assigned, never used
|
||||
//MIGR::infinidb_vtable.isUnion = false;
|
||||
gwi.cs_vtable_is_update_with_derive = true;
|
||||
gwi.fatalParseError = true;
|
||||
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_OUTER_JOIN_SUBSELECT);
|
||||
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (embeddingSet.find(table_ptr->embedding) != embeddingSet.end())
|
||||
continue;
|
||||
// build outerjoinon filter
|
||||
ParseTree* filters = NULL, *ptp = NULL, *rhs = NULL;
|
||||
|
||||
gwi_outer.innerTables.clear();
|
||||
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)
|
||||
while (!gwi_outer.ptWorkStack.empty())
|
||||
{
|
||||
if (!curr->outer_join) // only handle nested JOIN for now
|
||||
{
|
||||
gwi_outer.innerTables.clear();
|
||||
Item_cond* expr = reinterpret_cast<Item_cond*>(curr->on_expr);
|
||||
filters = gwi_outer.ptWorkStack.top();
|
||||
gwi_outer.ptWorkStack.pop();
|
||||
|
||||
#ifdef DEBUG_WALK_COND
|
||||
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
|
||||
#endif
|
||||
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
|
||||
}
|
||||
if (gwi_outer.ptWorkStack.empty())
|
||||
break;
|
||||
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -6289,16 +6175,13 @@ void setExecutionParams(gp_walk_info &gwi, SCSEP &csep)
|
||||
* FROM part of the query.
|
||||
* isUnion tells that CS processes FROM taken from UNION UNIT.
|
||||
* The notion is described in MDB code.
|
||||
* on_expr_list ON expressions used in OUTER JOINs. These are
|
||||
* later used in processWhere()
|
||||
* RETURNS
|
||||
* error id as an int
|
||||
***********************************************************/
|
||||
int processFrom(bool &isUnion,
|
||||
SELECT_LEX &select_lex,
|
||||
gp_walk_info &gwi,
|
||||
SCSEP &csep,
|
||||
List<Item> &on_expr_list)
|
||||
SCSEP &csep)
|
||||
{
|
||||
// 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
|
||||
@ -6328,12 +6211,6 @@ int processFrom(bool &isUnion,
|
||||
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);
|
||||
if (lower_case_table_names)
|
||||
{
|
||||
@ -6483,15 +6360,12 @@ int processFrom(bool &isUnion,
|
||||
* DESCRIPTION:
|
||||
* This function processes conditions from either JOIN->conds
|
||||
* or SELECT_LEX->where|prep_where
|
||||
* on_expr_list ON expressions used in OUTER JOINs. These are
|
||||
* populated used in processFrom()
|
||||
* RETURNS
|
||||
* error id as an int
|
||||
***********************************************************/
|
||||
int processWhere(SELECT_LEX &select_lex,
|
||||
gp_walk_info &gwi,
|
||||
SCSEP &csep,
|
||||
List<Item> &on_expr_list,
|
||||
const std::vector<COND*>& condStack)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
#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
|
||||
// use a recursive function.
|
||||
#ifdef OUTER_JOIN_DEBUG
|
||||
@ -6694,26 +6547,31 @@ int processWhere(SELECT_LEX &select_lex,
|
||||
}
|
||||
#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
|
||||
// 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)
|
||||
if ((failed = buildJoin(gwi, select_lex.top_join_list, outerJoinStack)))
|
||||
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* outerJoinFilters = 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
|
||||
// ptWorkStack empty, the item is in rcWorkStack.
|
||||
@ -6734,13 +6592,45 @@ int processWhere(SELECT_LEX &select_lex,
|
||||
break;
|
||||
|
||||
ptp = new ParseTree(new LogicOperator("and"));
|
||||
ptp->right(filters);
|
||||
lhs = gwi.ptWorkStack.top();
|
||||
ptp->left(filters);
|
||||
rhs = gwi.ptWorkStack.top();
|
||||
gwi.ptWorkStack.pop();
|
||||
ptp->left(lhs);
|
||||
ptp->right(rhs);
|
||||
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)
|
||||
{
|
||||
csep->filters(filters);
|
||||
@ -6928,15 +6818,15 @@ int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex,
|
||||
CalpontSelectExecutionPlan::SelectList derivedTbList;
|
||||
// @bug 1796. Remember table order on the FROM list.
|
||||
gwi.clauseType = FROM;
|
||||
List<Item> on_expr_list;
|
||||
if ((rc = processFrom(isUnion, select_lex, gwi, csep, on_expr_list)))
|
||||
if ((rc = processFrom(isUnion, select_lex, gwi, csep)))
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool unionSel = (!isUnion && select_lex.master_unit()->is_unit_op()) ? true : false;
|
||||
|
||||
gwi.clauseType = WHERE;
|
||||
if ((rc = processWhere(select_lex, gwi, csep, on_expr_list, condStack)))
|
||||
if ((rc = processWhere(select_lex, gwi, csep, condStack)))
|
||||
{
|
||||
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;
|
||||
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;
|
||||
|
||||
// @bug5764. build outer join for view, make sure outerjoin filter is appended
|
||||
// to the end of the filter list.
|
||||
for (uint i = 0; i < gwi.viewList.size(); i++)
|
||||
if (gwi.subQuery)
|
||||
{
|
||||
failed = gwi.viewList[i]->processOuterJoin(gwi);
|
||||
for (uint i = 0; i < gwi.viewList.size(); i++)
|
||||
{
|
||||
failed = gwi.viewList[i]->processJoin(gwi, outerJoinStack);
|
||||
|
||||
if (failed)
|
||||
break;
|
||||
if (failed)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed != 0)
|
||||
return failed;
|
||||
|
||||
ParseTree* filters = NULL;
|
||||
ParseTree* outerJoinFilters = 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
|
||||
// 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;
|
||||
|
||||
ptp = new ParseTree(new LogicOperator("and"));
|
||||
//ptp->left(filters);
|
||||
ptp->right(filters);
|
||||
lhs = gwi.ptWorkStack.top();
|
||||
ptp->left(filters);
|
||||
rhs = gwi.ptWorkStack.top();
|
||||
gwi.ptWorkStack.pop();
|
||||
//ptp->right(rhs);
|
||||
ptp->left(lhs);
|
||||
ptp->right(rhs);
|
||||
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)
|
||||
{
|
||||
csep->filters(filters);
|
||||
|
@ -373,7 +373,7 @@ bool buildRowColumnFilter(gp_walk_info* gwip, execplan::RowColumn* rhs, execplan
|
||||
bool buildPredicateItem(Item_func* ifp, gp_walk_info* gwip);
|
||||
void collectAllCols(gp_walk_info& gwi, Item_field* ifp);
|
||||
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);
|
||||
bool buildConstPredicate(Item_func* ifp, execplan::ReturnedColumn* rhs, gp_walk_info* gwip);
|
||||
execplan::CalpontSystemCatalog::ColType fieldType_MysqlToIDB (const Field* field);
|
||||
|
@ -44,7 +44,8 @@ using namespace execplan;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public:
|
||||
parent select.
|
||||
*/
|
||||
void transform();
|
||||
uint32_t processOuterJoin(gp_walk_info& gwi);
|
||||
uint32_t processJoin(gp_walk_info& gwi, std::stack<execplan::ParseTree*>&);
|
||||
|
||||
private:
|
||||
SELECT_LEX fSelect;
|
||||
|
Reference in New Issue
Block a user