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
Merge pull request #1912 from tntnatbry/MCOL-4680-dev
MCOL-4680 FROM subquery containing nested joins returns an error.
This commit is contained in:
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
29
mysql-test/columnstore/basic/r/mcol_4680.result
Normal file
29
mysql-test/columnstore/basic/r/mcol_4680.result
Normal 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;
|
41
mysql-test/columnstore/basic/t/mcol_4680.test
Normal file
41
mysql-test/columnstore/basic/t/mcol_4680.test
Normal 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;
|
Reference in New Issue
Block a user