diff --git a/dbcon/mysql/ha_mcs_execplan.cpp b/dbcon/mysql/ha_mcs_execplan.cpp index fe896f774..d5d4a9128 100755 --- a/dbcon/mysql/ha_mcs_execplan.cpp +++ b/dbcon/mysql/ha_mcs_execplan.cpp @@ -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& join_list, + std::set& leafTables) { - TABLE_LIST* table; - List_iterator li(table_ptr->nested_join->join_list); + TABLE_LIST *table; + List_iterator 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 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(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& join_list, + std::stack& outerJoinStack) { - // check non-collapsed outer join - // this set contains all processed embedded joins. duplicate joins are ignored - set 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 tmpVec; + TABLE_LIST *table; + List_iterator 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(table_ptr->on_expr); - gwi_outer.innerTables.insert(tan); + Item_cond* expr = reinterpret_cast(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 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::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 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(table_ptr->embedding->on_expr); - -#ifdef DEBUG_WALK_COND - cerr << "inner tables: " << endl; - set::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* inners = &(table_ptr->embedding->nested_join->join_list); - List_iterator_fast 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(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 &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 &on_expr_list, const std::vector& condStack) { JOIN* join = select_lex.join; @@ -6609,27 +6483,6 @@ int processWhere(SELECT_LEX &select_lex, (dynamic_cast(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 on_expr_it(on_expr_list); - Item_cond *on_expr = NULL; - while((on_expr = reinterpret_cast(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 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 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 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); diff --git a/dbcon/mysql/ha_mcs_impl_if.h b/dbcon/mysql/ha_mcs_impl_if.h index 41912e70f..eb672c649 100644 --- a/dbcon/mysql/ha_mcs_impl_if.h +++ b/dbcon/mysql/ha_mcs_impl_if.h @@ -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& join_list, std::stack& 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); diff --git a/dbcon/mysql/ha_view.cpp b/dbcon/mysql/ha_view.cpp index c804c5410..321e645ac 100644 --- a/dbcon/mysql/ha_view.cpp +++ b/dbcon/mysql/ha_view.cpp @@ -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& join_list, + std::stack& 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& outerJoinStack) { - return buildOuterJoin(gwi, fSelect); + return buildJoin(gwi, fSelect.top_join_list, outerJoinStack); } } diff --git a/dbcon/mysql/ha_view.h b/dbcon/mysql/ha_view.h index fdd1c8088..033a3aa51 100644 --- a/dbcon/mysql/ha_view.h +++ b/dbcon/mysql/ha_view.h @@ -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&); private: SELECT_LEX fSelect; diff --git a/mysql-test/columnstore/basic/r/mcol_4680.result b/mysql-test/columnstore/basic/r/mcol_4680.result new file mode 100644 index 000000000..3da7abad3 --- /dev/null +++ b/mysql-test/columnstore/basic/r/mcol_4680.result @@ -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; diff --git a/mysql-test/columnstore/basic/t/mcol_4680.test b/mysql-test/columnstore/basic/t/mcol_4680.test new file mode 100644 index 000000000..7964dea60 --- /dev/null +++ b/mysql-test/columnstore/basic/t/mcol_4680.test @@ -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;