diff --git a/dbcon/mysql/CMakeLists.txt b/dbcon/mysql/CMakeLists.txt index 104e6bf6e..7721aed80 100644 --- a/dbcon/mysql/CMakeLists.txt +++ b/dbcon/mysql/CMakeLists.txt @@ -20,6 +20,9 @@ SET ( libcalmysql_SRCS ha_pseudocolumn.cpp) add_definitions(-DMYSQL_DYNAMIC_PLUGIN) +add_definitions(-DDEBUG_WALK_COND) +add_definitions(-DINFINIDB_DEBUG) +#add_definitions(-DOUTER_JOIN_DEBUG) set_source_files_properties(ha_calpont.cpp PROPERTIES COMPILE_FLAGS "-fno-rtti -fno-implicit-templates") diff --git a/dbcon/mysql/ha_calpont.h b/dbcon/mysql/ha_calpont.h index c3f2573e2..f15078fad 100644 --- a/dbcon/mysql/ha_calpont.h +++ b/dbcon/mysql/ha_calpont.h @@ -254,16 +254,29 @@ class ha_calpont_group_by_handler: public group_by_handler // ha_calpont_group_by_handler(THD *thd_arg, List *fields_arg, // TABLE_LIST *table_list_arg, Query *query) ha_calpont_group_by_handler(THD *thd_arg, Query *query) - : group_by_handler(thd_arg, calpont_hton), fields(query->select), - table_list(query->from), query(query) {} - ~ha_calpont_group_by_handler() {} + : group_by_handler(thd_arg, calpont_hton), + select(query->select), + table_list(query->from), + distinct(query->distinct), + where(query->where), + group_by(query->group_by), + order_by(query->order_by), + having(query->having), + query(query) + { } + ~ha_calpont_group_by_handler() { } int init_scan(); int next_row(); int end_scan(); - List *fields; + List *select; TABLE_LIST *table_list; - bool first_row; - Query *query; + bool distinct; + Item *where; + ORDER *group_by; + ORDER *order_by; + Item *having; + bool first_row; // useless by now + Query *query; // useless by now }; #endif //HA_CALPONT_H__ diff --git a/dbcon/mysql/ha_calpont_execplan.cpp b/dbcon/mysql/ha_calpont_execplan.cpp index 8c0934f4c..5bed8e88c 100644 --- a/dbcon/mysql/ha_calpont_execplan.cpp +++ b/dbcon/mysql/ha_calpont_execplan.cpp @@ -7937,6 +7937,33 @@ int cp_get_table_plan(THD* thd, SCSEP& csep, cal_table_info& ti) return 0; } +int cp_get_group_plan(THD* thd, SCSEP& csep, cal_table_info& ti, cal_group_info& gi ) +{ + LEX* lex = thd->lex; + idbassert(lex != 0); + + SELECT_LEX select_lex = lex->select_lex; + gp_walk_info gwi; + gwi.thd = thd; + gwi.thd->infinidb_vtable.vtable_state = THD::INFINIDB_CREATE_VTABLE; + int status = getGroupPlan(gwi, select_lex, csep, gi); + + if (status > 0) + return ER_INTERNAL_ERROR; + else if (status < 0) + return status; + + cerr << "---------------- cp_get_group_plan EXECUTION PLAN ----------------" << endl; + cerr << *csep << endl ; + cerr << "-------------- EXECUTION PLAN END --------------\n" << endl; + + // Derived table projection and filter optimization. + //derivedTableOptimization(csep); + + return 0; +} + +/* int cp_get_group_plan(THD* thd, SCSEP& csep, cal_table_info& ti ) { gp_walk_info* gwi = ti.condInfo; @@ -7952,6 +7979,7 @@ int cp_get_group_plan(THD* thd, SCSEP& csep, cal_table_info& ti ) uint32_t sessionID = csep->sessionID(); gwi->sessionid = sessionID; TABLE* table = ti.msTablePtr; + TABLE_LIST* table_list = ti.groupByTables; boost::shared_ptr csc = CalpontSystemCatalog::makeCalpontSystemCatalog(sessionID); csc->identity(CalpontSystemCatalog::FE); @@ -7961,10 +7989,11 @@ int cp_get_group_plan(THD* thd, SCSEP& csep, cal_table_info& ti ) Field** f_ptr, *field; gwi->columnMap.clear(); - const CalpontSystemCatalog::TableAliasName tblAliasName= + CalpontSystemCatalog::TableAliasName tan= make_aliastable(table->s->db.str, table->s->table_name.str, table->alias.c_ptr()); - gwi->tbList.push_back(tblAliasName); + gwi->tbList.push_back(tan); + gwi->tableMap[tan] = make_pair(0, table_list); while ((item = it++)) { @@ -7976,38 +8005,59 @@ int cp_get_group_plan(THD* thd, SCSEP& csep, cal_table_info& ti ) assert (rc); boost::shared_ptr sprc(rc); gwi->returnedCols.push_back(sprc); - arg0=((Item_sum*) item)->get_arg(0); - field=((Item_field*) arg0)->field; - gwi->columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(string(field->field_name), sprc)); - //arg0=((Item_sum*) item)->get_arg(0); - //field=((Item_field*) arg0)->field; - } - -/* - for (f_ptr = table->field ; (field = *f_ptr) ; f_ptr++) - { - if (bitmap_is_set(read_set, field->field_index)) - { - SimpleColumn* sc = new SimpleColumn(table->s->db.str, table->s->table_name.str, field->field_name, sessionID); - string alias(table->alias.c_ptr()); - sc->tableAlias(lower(alias)); - assert (sc); - boost::shared_ptr spsc(sc); - gwi->returnedCols.push_back(spsc); - gwi->columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(string(field->field_name), spsc)); - } - } -*/ - if (gwi->columnMap.empty()) - { - CalpontSystemCatalog::TableName tn = make_table(table->s->db.str, table->s->table_name.str); - CalpontSystemCatalog::TableAliasName tan = make_aliastable(table->s->db.str, table->s->table_name.str, table->alias.c_ptr()); - SimpleColumn* sc = getSmallestColumn(csc, tn, tan, table, *gwi); - SRCP srcp(sc); - gwi->columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(sc->columnName(), srcp)); - gwi->returnedCols.push_back(srcp); } + // TO DO add table into tableMap + // TO DO add the column only if there is no any for a table. + TableMap::iterator tb_iter = gwi->tableMap.begin(); + try + { + for (; tb_iter != gwi->tableMap.end(); tb_iter++) + { + if ((*tb_iter).second.first == 1) continue; + + CalpontSystemCatalog::TableAliasName tan = (*tb_iter).first; + CalpontSystemCatalog::TableName tn = make_table((*tb_iter).first.schema, (*tb_iter).first.table); + SimpleColumn* sc = getSmallestColumn(csc, tn, tan, (*tb_iter).second.second->table, *gwi); + SRCP srcp(sc); + gwi->columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(sc->columnName(), srcp)); + (*tb_iter).second.first = 1; + } + } + catch (runtime_error& e) + { + setError(gwi->thd, ER_INTERNAL_ERROR, e.what(), *gwi); + CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID); + return ER_INTERNAL_ERROR; + } + catch (...) + { + string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR); + setError(gwi->thd, ER_INTERNAL_ERROR, emsg, *gwi); + CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID); + return ER_INTERNAL_ERROR; + } + + //if (gwi->columnMap.empty()) + //{ + // CalpontSystemCatalog::TableName tn = make_table(table->s->db.str, table->s->table_name.str); + // CalpontSystemCatalog::TableAliasName tan = make_aliastable(table->s->db.str, table->s->table_name.str, table->alias.c_ptr()); + // SimpleColumn* sc = getSmallestColumn(csc, tn, tan, table, *gwi); + // SRCP srcp(sc); + // gwi->columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(sc->columnName(), srcp)); + // gwi->returnedCols.push_back(srcp); + //} + + + CalpontSystemCatalog::TableName tn = make_table(table->s->db.str, table->s->table_name.str); + //SimpleColumn* minSc = getSmallestColumn(csc, tn, tan, table, *gwi); + SRCP minSc; + minSc = gwi->returnedCols[0]; + std::vector::iterator coliter; + for (coliter = gwi->count_asterisk_list.begin(); coliter != gwi->count_asterisk_list.end(); ++coliter) + { + (*coliter)->functionParms(minSc); + } // get filter if (ti.condInfo) @@ -8051,7 +8101,7 @@ int cp_get_group_plan(THD* thd, SCSEP& csep, cal_table_info& ti ) csep->returnedCols(gwi->returnedCols); csep->columnMap(gwi->columnMap); CalpontSelectExecutionPlan::TableList tblist; - tblist.push_back(tblAliasName); + tblist.push_back(tan); csep->tableList(tblist); // @bug 3321. Set max number of blocks in a dictionary file to be scanned for filtering @@ -8059,5 +8109,2386 @@ int cp_get_group_plan(THD* thd, SCSEP& csep, cal_table_info& ti ) return 0; } +*/ + +int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_group_info& gi, bool isUnion) +{ +#ifdef DEBUG_WALK_COND + cerr << "getGroupPlan()" << endl; +#endif + + // by pass the derived table resolve phase of mysql + /*if (!(((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || + ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || + ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || + ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI ) ) && gwi.thd->derived_tables_processing) + { + gwi.thd->infinidb_vtable.isUnion = false; + return -1; + } + */ + // rollup is currently not supported + if (select_lex.olap == ROLLUP_TYPE) + { + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_ROLLUP_NOT_SUPPORT); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + gwi.internalDecimalScale = (gwi.thd->variables.infinidb_use_decimal_scale ? gwi.thd->variables.infinidb_decimal_scale : -1); + gwi.subSelectType = csep->subType(); + + JOIN* join = select_lex.join; + Item_cond* icp = 0; + /* MCOL-1052 + if (join != 0) + icp = reinterpret_cast(join->conds); + + // if icp is null, try to find the where clause other where + if (!join && gwi.thd->lex->derived_tables) + { + if (select_lex.prep_where) + icp = (Item_cond*)(select_lex.prep_where); + else if (select_lex.where) + icp = (Item_cond*)(select_lex.where); + } + else if (!join && ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || + ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || + ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || + ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI ))) + { + icp = reinterpret_cast(select_lex.where); + } + */ + // MCOL-1052 + if ( gi.groupByWhere ) + icp = reinterpret_cast(gi.groupByWhere); + + uint32_t sessionID = csep->sessionID(); + gwi.sessionid = sessionID; + boost::shared_ptr csc = CalpontSystemCatalog::makeCalpontSystemCatalog(sessionID); + csc->identity(CalpontSystemCatalog::FE); + gwi.csc = csc; + + // @bug 2123. Override large table estimate if infinidb_ordered hint was used. + // @bug 2404. Always override if the infinidb_ordered_only variable is turned on. + if (gwi.thd->infinidb_vtable.override_largeside_estimate || gwi.thd->variables.infinidb_ordered_only) + csep->overrideLargeSideEstimate(true); + + // @bug 5741. Set a flag when in Local PM only query mode + csep->localQuery(gwi.thd->variables.infinidb_local_query); + + // @bug 3321. Set max number of blocks in a dictionary file to be scanned for filtering + csep->stringScanThreshold(gwi.thd->variables.infinidb_string_scan_threshold); + + csep->stringTableThreshold(gwi.thd->variables.infinidb_stringtable_threshold); + + csep->djsSmallSideLimit(gwi.thd->variables.infinidb_diskjoin_smallsidelimit * 1024ULL * 1024); + csep->djsLargeSideLimit(gwi.thd->variables.infinidb_diskjoin_largesidelimit * 1024ULL * 1024); + csep->djsPartitionSize(gwi.thd->variables.infinidb_diskjoin_bucketsize * 1024ULL * 1024); + + if (gwi.thd->variables.infinidb_um_mem_limit == 0) + csep->umMemLimit(numeric_limits::max()); + else + csep->umMemLimit(gwi.thd->variables.infinidb_um_mem_limit * 1024ULL * 1024); + + // 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 + TABLE_LIST* table_ptr = gi.groupByTables; + CalpontSelectExecutionPlan::SelectList derivedTbList; + +// DEBUG +#ifdef DEBUG_WALK_COND + List_iterator sj_list_it(select_lex.sj_nests); + TABLE_LIST* sj_nest; + + while ((sj_nest = sj_list_it++)) + { + cerr << sj_nest->db << "." << sj_nest->table_name << endl; + } + +#endif + + // @bug 1796. Remember table order on the FROM list. + gwi.clauseType = FROM; + + try + { + for (; table_ptr; table_ptr = table_ptr->next_local) + { + // mysql put vtable here for from sub. we ignore it + //if (string(table_ptr->table_name).find("$vtable") != string::npos) + // continue; + + // Until we handle recursive cte: + // Checking here ensures we catch all with clauses in the query. + if (table_ptr->is_recursive_with_table()) + { + gwi.fatalParseError = true; + gwi.parseErrorText = "Recursive CTE"; + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + string viewName = getViewName(table_ptr); + + // @todo process from subquery + if (table_ptr->derived) + { + String str; + (table_ptr->derived->first_select())->print(gwi.thd, &str, QT_INFINIDB_DERIVED); + + SELECT_LEX* select_cursor = table_ptr->derived->first_select(); + FromSubQuery fromSub(gwi, select_cursor); + string alias(table_ptr->alias); + fromSub.alias(lower(alias)); + + CalpontSystemCatalog::TableAliasName tn = make_aliasview("", "", alias, viewName); + // @bug 3852. check return execplan + SCSEP plan = fromSub.transform(); + + if (!plan) + { + setError(gwi.thd, ER_INTERNAL_ERROR, fromSub.gwip().parseErrorText, gwi); + CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID); + return ER_INTERNAL_ERROR; + } + + gwi.derivedTbList.push_back(plan); + gwi.tbList.push_back(tn); + CalpontSystemCatalog::TableAliasName tan = make_aliastable("", alias, alias); + gwi.tableMap[tan] = make_pair(0, table_ptr); + gwi.thd->infinidb_vtable.isUnion = true; //by-pass the 2nd pass of rnd_init + } + else if (table_ptr->view) + { + View* view = new View(table_ptr->view->select_lex, &gwi); + CalpontSystemCatalog::TableAliasName tn = make_aliastable(table_ptr->db, table_ptr->table_name, table_ptr->alias); + view->viewName(tn); + gwi.viewList.push_back(view); + view->transform(); + } + else + { + // check foreign engine tables + bool infiniDB = (table_ptr->table ? isInfiniDB(table_ptr->table) : true); + + // trigger system catalog cache + if (infiniDB) + csc->columnRIDs(make_table(table_ptr->db, table_ptr->table_name), true); + + string table_name = table_ptr->table_name; + + // @bug5523 + if (table_ptr->db && strcmp(table_ptr->db, "information_schema") == 0) + table_name = (table_ptr->schema_table_name ? table_ptr->schema_table_name : table_ptr->alias); + + CalpontSystemCatalog::TableAliasName tn = make_aliasview(table_ptr->db, table_name, table_ptr->alias, viewName, infiniDB); + gwi.tbList.push_back(tn); + CalpontSystemCatalog::TableAliasName tan = make_aliastable(table_ptr->db, table_name, table_ptr->alias, infiniDB); + gwi.tableMap[tan] = make_pair(0, table_ptr); +#ifdef DEBUG_WALK_COND + cerr << tn << endl; +#endif + } + } + + if (gwi.fatalParseError) + { + setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi); + return ER_INTERNAL_ERROR; + } + } + catch (IDBExcept& ie) + { + setError(gwi.thd, ER_INTERNAL_ERROR, ie.what(), gwi); + CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID); + // @bug 3852. set error status for gwi. + gwi.fatalParseError = true; + gwi.parseErrorText = ie.what(); + return ER_INTERNAL_ERROR; + } + catch (...) + { + string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR); + // @bug3852 set error status for gwi. + gwi.fatalParseError = true; + gwi.parseErrorText = emsg; + setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi); + CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID); + return ER_INTERNAL_ERROR; + } + + csep->tableList(gwi.tbList); + + bool unionSel = false; + + if (!isUnion && select_lex.master_unit()->is_union()) + { + gwi.thd->infinidb_vtable.isUnion = true; + CalpontSelectExecutionPlan::SelectList unionVec; + SELECT_LEX* select_cursor = select_lex.master_unit()->first_select(); + unionSel = true; + uint8_t distUnionNum = 0; + + for (SELECT_LEX* sl = select_cursor; sl; sl = sl->next_select()) + { + SCSEP plan(new CalpontSelectExecutionPlan()); + plan->txnID(csep->txnID()); + plan->verID(csep->verID()); + plan->sessionID(csep->sessionID()); + plan->traceFlags(csep->traceFlags()); + plan->data(csep->data()); + + // @bug 3853. When one or more sides or union queries contain derived tables, + // sl->join->zero_result_cause is not trustable. Since we've already handled + // constant filter now (0/1), we can relax the following checking. + // @bug 2547. ignore union unit of zero result set case +// if (sl->join) +// { +// sl->join->optimize(); + // @bug 3067. not clear MySQL's behavior. when in subquery, this variable + // is not trustable. +// if (sl->join->zero_result_cause && !gwi.subQuery) +// continue; +// } + + // gwi for the union unit + gp_walk_info union_gwi; + union_gwi.thd = gwi.thd; + uint32_t err = 0; + + if ((err = getSelectPlan(union_gwi, *sl, plan, unionSel)) != 0) + return err; + + unionVec.push_back(SCEP(plan)); + + // distinct union num + if (sl == select_lex.master_unit()->union_distinct) + distUnionNum = unionVec.size(); + + /*#ifdef DEBUG_WALK_COND + IDEBUG( cout << ">>>> UNION DEBUG" << endl ); + JOIN* join = sl->join; + Item_cond* icp = 0; + if (join != 0) + icp = reinterpret_cast(join->conds); + if (icp) + icp->traverse_cond(debug_walk, &gwi, Item::POSTFIX); + IDEBUG ( cout << *plan << endl ); + IDEBUG ( cout << "<<<unionVec(unionVec); + csep->distinctUnionNum(distUnionNum); + + if (unionVec.empty()) + gwi.thd->infinidb_vtable.impossibleWhereOnUnion = true; + } + + gwi.clauseType = WHERE; + + if (icp) + { + // MCOL-1052 The condition could be useless. +// MariaDB bug 624 - without the fix_fields call, delete with join may error with "No query step". +//#if MYSQL_VERSION_ID < 50172 + //@bug 3039. fix fields for constants + if (!icp->fixed) + { + icp->fix_fields(gwi.thd, (Item**)&icp); + } + +//#endif + gwi.fatalParseError = false; +#ifdef DEBUG_WALK_COND + cerr << "------------------ WHERE -----------------------" << endl; + icp->traverse_cond(debug_walk, &gwi, Item::POSTFIX); + cerr << "------------------------------------------------\n" << endl; +#endif + + icp->traverse_cond(gp_walk, &gwi, Item::POSTFIX); + + if (gwi.fatalParseError) + { + // if this is dervied table process phase, mysql may have not developed the plan + // completely. Do not error and eventually mysql will call JOIN::exec() again. + // related to bug 2922. Need to find a way to skip calling rnd_init for derived table + // processing. + if (gwi.thd->derived_tables_processing) + { + gwi.thd->infinidb_vtable.isUnion = false; + gwi.thd->infinidb_vtable.isUpdateWithDerive = true; + return -1; + } + + setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi); + return ER_INTERNAL_ERROR; + } + } + else if (join && join->zero_result_cause) + { + gwi.rcWorkStack.push(new ConstantColumn((int64_t)0, ConstantColumn::NUM)); + } + + + // ZZ - the followinig debug shows the structure of nested outer join. should + // use a recursive function. +#ifdef OUTER_JOIN_DEBUG + List* tables = &(select_lex.top_join_list); + List_iterator_fast ti(*tables); + //TABLE_LIST *inner; + //TABLE_LIST **table= (TABLE_LIST **)gwi.thd->alloc(sizeof(TABLE_LIST*) * tables->elements); + //for (TABLE_LIST **t= table + (tables->elements - 1); t >= table; t--) + // *t= ti++; + + //DBUG_ASSERT(tables->elements >= 1); + + //TABLE_LIST **end= table + tables->elements; + //for (TABLE_LIST **tbl= table; tbl < end; tbl++) + TABLE_LIST* curr; + + while ((curr = ti++)) + { + TABLE_LIST* curr = *tbl; + + if (curr->table_name) + cout << curr->table_name << " "; + else + cout << curr->alias << endl; + + if (curr->outer_join) + cout << " is inner table" << endl; + else if (curr->straight) + cout << "straight_join" << endl; + else + cout << "join" << endl; + + if (curr->nested_join) + { + List* inners = &(curr->nested_join->join_list); + List_iterator_fast li(*inners); + TABLE_LIST** inner = (TABLE_LIST**)gwi.thd->alloc(sizeof(TABLE_LIST*) * inners->elements); + + for (TABLE_LIST** t = inner + (inners->elements - 1); t >= inner; t--) + *t = li++; + + TABLE_LIST** end1 = inner + inners->elements; + + for (TABLE_LIST** tb = inner; tb < end1; tb++) + { + TABLE_LIST* curr1 = *tb; + cout << curr1->alias << endl; + + if (curr1->sj_on_expr) + { + curr1->sj_on_expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX); + } + } + } + + if (curr->sj_on_expr) + { + curr->sj_on_expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX); + } + } + +#endif + // MCOL-1052 + uint32_t failed = buildOuterJoin(gwi, select_lex); + + 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++) + { + failed = gwi.viewList[i]->processOuterJoin(gwi); + + if (failed) + break; + } + + if (failed != 0) + return failed; + + ParseTree* filters = NULL; + ParseTree* ptp = NULL; + ParseTree* lhs = NULL; + + // @bug 2932. for "select * from region where r_name" case. if icp not null and + // ptWorkStack empty, the item is in rcWorkStack. + // MySQL 5.6 (MariaDB?). when icp is null and zero_result_cause is set, a constant 0 + // is pushed to rcWorkStack. + if (/*icp && */gwi.ptWorkStack.empty() && !gwi.rcWorkStack.empty()) + { + filters = new ParseTree(gwi.rcWorkStack.top()); + gwi.rcWorkStack.pop(); + } + + while (!gwi.ptWorkStack.empty()) + { + filters = gwi.ptWorkStack.top(); + gwi.ptWorkStack.pop(); + + if (gwi.ptWorkStack.empty()) + break; + + ptp = new ParseTree(new LogicOperator("and")); + //ptp->left(filters); + ptp->right(filters); + lhs = gwi.ptWorkStack.top(); + gwi.ptWorkStack.pop(); + //ptp->right(rhs); + ptp->left(lhs); + gwi.ptWorkStack.push(ptp); + } + + if (filters) + { + csep->filters(filters); + filters->drawTree("/tmp/filter1.dot"); + } + + gwi.clauseType = SELECT; +#ifdef DEBUG_WALK_COND + { + cerr << "------------------- SELECT --------------------" << endl; + List_iterator_fast it(*gi.groupByFields); + Item* item; + + while ((item = it++)) + { + debug_walk(item, 0); + } + + cout << "-----------------------------------------------\n" << endl; + } +#endif + + // populate returnedcolumnlist and columnmap + List_iterator_fast it(*gi.groupByFields); + Item* item; + vector funcFieldVec; + string sel_cols_in_create; + string sel_cols_in_select; + bool redo = false; + + // empty rcWorkStack and ptWorkStack. They should all be empty by now. + clearStacks(gwi); + + // indicate the starting pos of scalar returned column, because some join column + // has been inserted to the returned column list. + if (gwi.subQuery) + { + ScalarSub* scalar = dynamic_cast(gwi.subQuery); + + if (scalar) + scalar->returnedColPos(gwi.additionalRetCols.size()); + } + + CalpontSelectExecutionPlan::SelectList selectSubList; + + while ((item = it++)) + { + string itemAlias = (item->name ? item->name : ""); + + // @bug 5916. Need to keep checking until getting concret item in case + // of nested view. + while (item->type() == Item::REF_ITEM) + { + Item_ref* ref = (Item_ref*)item; + item = (*(ref->ref)); + } + + Item::Type itype = item->type(); + + switch (itype) + { + case Item::FIELD_ITEM: + { + Item_field* ifp = (Item_field*)item; + SimpleColumn* sc = NULL; + + if (ifp->field_name && string(ifp->field_name) == "*") + { + collectAllCols(gwi, ifp); + break; + } + + sc = buildSimpleColumn(ifp, gwi); + + if (sc) + { + boost::shared_ptr spsc(sc); + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + string fullname; + String str; + ifp->print(&str, QT_INFINIDB_NO_QUOTE); + fullname = str.c_ptr(); + + //sel_cols_in_create += fullname; + if (ifp->is_autogenerated_name) // no alias + { + sel_cols_in_create += fullname + " `" + escapeBackTick(str.c_ptr()) + "`"; + sc->alias(fullname); + } + else // alias + { + if (!itemAlias.empty()) + sc->alias(itemAlias); + + sel_cols_in_create += fullname + " `" + escapeBackTick(sc->alias().c_str()) + "`"; + } + + if (ifp->is_autogenerated_name) + gwi.selectCols.push_back("`" + escapeBackTick(fullname.c_str()) + "`" + " `" + + escapeBackTick(itemAlias.empty() ? ifp->name : itemAlias.c_str()) + "`"); + else + gwi.selectCols.push_back("`" + escapeBackTick((itemAlias.empty() ? ifp->name : itemAlias.c_str())) + "`"); + + gwi.returnedCols.push_back(spsc); + + gwi.columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name), spsc)); + TABLE_LIST* tmp = 0; + + if (ifp->cached_table) + tmp = ifp->cached_table; + + gwi.tableMap[make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias(), sc->isInfiniDB())] = + make_pair(1, tmp); + } + else + { + setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi); + delete sc; + return ER_INTERNAL_ERROR; + } + + break; + } + + //aggregate column + case Item::SUM_FUNC_ITEM: + { + ReturnedColumn* ac = buildAggregateColumn(item, gwi); + + if (gwi.fatalParseError) + { + // e.g., non-support ref column + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + delete ac; + return ER_CHECK_NOT_IMPLEMENTED; + } + + // add this agg col to returnedColumnList + boost::shared_ptr spac(ac); + gwi.returnedCols.push_back(spac); + gwi.selectCols.push_back('`' + escapeBackTick(spac->alias().c_str()) + '`'); + String str(256); + item->print(&str, QT_INFINIDB_NO_QUOTE); + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + sel_cols_in_create += string(str.c_ptr()) + " `" + escapeBackTick(spac->alias().c_str()) + "`"; + break; + } + + case Item::FUNC_ITEM: + { + Item_func* ifp = reinterpret_cast(item); + + // @bug4383. error out non-support stored function + if (ifp->functype() == Item_func::FUNC_SP) + { + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_SP_FUNCTION_NOT_SUPPORT); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + if (string(ifp->func_name()) == "xor") + { + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + uint16_t parseInfo = 0; + vector tmpVec; + bool hasNonSupportItem = false; + parse_item(ifp, tmpVec, hasNonSupportItem, parseInfo); + + if (ifp->has_subquery() || + string(ifp->func_name()) == string("") || + ifp->functype() == Item_func::NOT_ALL_FUNC || + parseInfo & SUB_BIT) + { + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SELECT_SUB); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + ReturnedColumn* rc = buildFunctionColumn(ifp, gwi, hasNonSupportItem); + SRCP srcp(rc); + + if (rc) + { + if (!hasNonSupportItem && !nonConstFunc(ifp) && !(parseInfo & AF_BIT) && tmpVec.size() == 0) + { + if (isUnion || unionSel || gwi.subSelectType != CalpontSelectExecutionPlan::MAIN_SELECT || + parseInfo & SUB_BIT ) //|| select_lex.group_list.elements != 0) + { + srcp.reset(buildReturnedColumn(item, gwi, gwi.fatalParseError)); + gwi.returnedCols.push_back(srcp); + + if (ifp->name) + srcp->alias(ifp->name); + + continue; + } + + if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || + ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || + ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || + ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI ) ) + { } + else + { + redo = true; + String str; + ifp->print(&str, QT_INFINIDB_NO_QUOTE); + gwi.selectCols.push_back(string(str.c_ptr()) + " " + "`" + escapeBackTick(item->name) + "`"); + } + + break; + } + + //SRCP srcp(rc); + gwi.returnedCols.push_back(srcp); + + if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI )) + { } + else + { + String str(256); + ifp->print(&str, QT_INFINIDB_NO_QUOTE); + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + sel_cols_in_create += string(str.c_ptr()) + " `" + ifp->name + "`"; + gwi.selectCols.push_back("`" + escapeBackTick(ifp->name) + "`"); + } + } + else // InfiniDB Non support functions still go through post process for now + { + hasNonSupportItem = false; + uint32_t before_size = funcFieldVec.size(); + parse_item(ifp, funcFieldVec, hasNonSupportItem, parseInfo); + uint32_t after_size = funcFieldVec.size(); + + // group by func and func in subquery can not be post processed + // @bug3881. set_user_var can not be treated as constant function + // @bug5716. Try to avoid post process function for union query. + if ((gwi.subQuery /*|| select_lex.group_list.elements != 0 */ || + !csep->unionVec().empty() || isUnion) && + !hasNonSupportItem && (after_size - before_size) == 0 && + !(parseInfo & AGG_BIT) && !(parseInfo & SUB_BIT) && + string(ifp->func_name()) != "set_user_var") + { + String val, *str = ifp->val_str(&val); + string valStr; + + if (str) + valStr.assign(str->ptr(), str->length()); + + ConstantColumn* cc = NULL; + + if (!str) + { + cc = new ConstantColumn("", ConstantColumn::NULLDATA); + } + else if (ifp->result_type() == STRING_RESULT) + { + cc = new ConstantColumn(valStr, ConstantColumn::LITERAL); + } + else if (ifp->result_type() == DECIMAL_RESULT) + { + cc = buildDecimalColumn(ifp, gwi); + } + else + { + cc = new ConstantColumn(valStr, ConstantColumn::NUM); + cc->resultType(colType_MysqlToIDB(item)); + } + + SRCP srcp(cc); + + if (ifp->name) + cc->alias(ifp->name); + + gwi.returnedCols.push_back(srcp); + + // clear the error set by buildFunctionColumn + gwi.fatalParseError = false; + gwi.parseErrorText = ""; + break; + } + else if (hasNonSupportItem || parseInfo & AGG_BIT || parseInfo & SUB_BIT || + (gwi.fatalParseError && gwi.subQuery)) + { + if (gwi.parseErrorText.empty()) + { + Message::Args args; + args.add(ifp->func_name()); + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORTED_FUNCTION, args); + } + + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + else if ( gwi.subQuery && (isPredicateFunction(ifp, &gwi) || ifp->type() == Item::COND_ITEM )) + { + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + //@Bug 3030 Add error check for dml statement + if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI ) ) + { + if ( after_size - before_size != 0 ) + { + gwi.parseErrorText = ifp->func_name(); + return -1; + } + } + + //@Bug 3021. Bypass postprocess for update and delete. + //if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI )) + //{} + else + { + // @bug 3881. Here is the real redo part. + redo = true; + // @bug 1706 + String funcStr; + ifp->print(&funcStr, QT_INFINIDB); + gwi.selectCols.push_back(string(funcStr.c_ptr()) + " `" + escapeBackTick(ifp->name) + "`"); + // clear the error set by buildFunctionColumn + gwi.fatalParseError = false; + gwi.parseErrorText = ""; + } + } + + break; + } + + case Item::INT_ITEM: + { + if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI )) + { } + else + { + // do not push the dummy column (mysql added) to returnedCol + if (item->name && string(item->name) == "Not_used") + continue; + + // @bug3509. Constant column is sent to ExeMgr now. + SRCP srcp(buildReturnedColumn(item, gwi, gwi.fatalParseError)); + + if (item->name) + srcp->alias(item->name); + + gwi.returnedCols.push_back(srcp); + + Item_int* isp = reinterpret_cast(item); + ostringstream oss; + oss << isp->value << " `" << escapeBackTick(srcp->alias().c_str()) << "`"; + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + sel_cols_in_create += oss.str(); + gwi.selectCols.push_back(oss.str()); + } + + break; + } + + case Item::STRING_ITEM: + { + if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI )) + { } + else + { + SRCP srcp(buildReturnedColumn(item, gwi, gwi.fatalParseError)); + gwi.returnedCols.push_back(srcp); + + if (item->name) + srcp->alias(item->name); + + Item_string* isp = reinterpret_cast(item); + String val, *str = isp->val_str(&val); + string valStr; + valStr.assign(str->ptr(), str->length()); + string name = "'" + valStr + "'" + " " + "`" + escapeBackTick(srcp->alias().c_str()) + "`"; + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + sel_cols_in_create += name; + gwi.selectCols.push_back(name); + } + + break; + } + + case Item::DECIMAL_ITEM: + { + if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI )) + { } + else + { + SRCP srcp(buildReturnedColumn(item, gwi, gwi.fatalParseError)); + gwi.returnedCols.push_back(srcp); + + if (item->name) + srcp->alias(item->name); + + Item_decimal* isp = reinterpret_cast(item); + String val, *str = isp->val_str(&val); + string valStr; + valStr.assign(str->ptr(), str->length()); + ostringstream oss; + oss << valStr.c_str() << " `" << escapeBackTick(srcp->alias().c_str()) << "`"; + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + sel_cols_in_create += oss.str(); + gwi.selectCols.push_back(oss.str()); + } + + break; + } + + case Item::NULL_ITEM: + { + if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI ) ) + { } + else + { + SRCP srcp(buildReturnedColumn(item, gwi, gwi.fatalParseError)); + gwi.returnedCols.push_back(srcp); + + if (item->name) + srcp->alias(item->name); + + string name = string("null `") + escapeBackTick(srcp->alias().c_str()) + string("`") ; + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + sel_cols_in_create += name; + gwi.selectCols.push_back("null"); + } + + break; + } + + case Item::SUBSELECT_ITEM: + { + Item_subselect* sub = (Item_subselect*)item; + + if (sub->substype() != Item_subselect::SINGLEROW_SUBS) + { + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SELECT_SUB); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + +#ifdef DEBUG_WALK_COND + cerr << "SELECT clause SUBSELECT Item: " << sub->substype() << endl; + JOIN* join = sub->get_select_lex()->join; + + if (join) + { + Item_cond* cond = reinterpret_cast(join->conds); + + if (cond) + cond->traverse_cond(debug_walk, &gwi, Item::POSTFIX); + } + + cerr << "Finish SELECT clause subselect item traversing" << endl; +#endif + SelectSubQuery* selectSub = new SelectSubQuery(gwi, sub); + //selectSub->gwip(&gwi); + SCSEP ssub = selectSub->transform(); + + if (!ssub || gwi.fatalParseError) + { + if (gwi.parseErrorText.empty()) + gwi.parseErrorText = "Unsupported Item in SELECT subquery."; + + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + selectSubList.push_back(ssub); + SimpleColumn* rc = new SimpleColumn(); + rc->colSource(rc->colSource() | SELECT_SUB); + + if (sub->get_select_lex()->get_table_list()) + rc->viewName(lower(getViewName(sub->get_select_lex()->get_table_list()))); + + if (sub->name) + rc->alias(sub->name); + + gwi.returnedCols.push_back(SRCP(rc)); + String str; + sub->get_select_lex()->print(gwi.thd, &str, QT_INFINIDB_NO_QUOTE); + sel_cols_in_create += "(" + string(str.c_ptr()) + ")"; + + if (sub->name) + { + sel_cols_in_create += "`" + escapeBackTick(sub->name) + "`"; + gwi.selectCols.push_back(sub->name); + } + else + { + sel_cols_in_create += "`" + escapeBackTick(str.c_ptr()) + "`"; + gwi.selectCols.push_back("`" + escapeBackTick(str.c_ptr()) + "`"); + } + + break; + } + + case Item::COND_ITEM: + { + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_FILTER_COND_EXP); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + case Item::EXPR_CACHE_ITEM: + { + printf("EXPR_CACHE_ITEM in getSelectPlan\n"); + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_UNKNOWN_COL); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + case Item::WINDOW_FUNC_ITEM: + { + SRCP srcp(buildWindowFunctionColumn(item, gwi, gwi.fatalParseError)); + + if (!srcp || gwi.fatalParseError) + { + if (gwi.parseErrorText.empty()) + gwi.parseErrorText = "Unsupported Item in SELECT subquery."; + + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + gwi.returnedCols.push_back(srcp); + break; + } + + default: + { + break; + } + } + } + + // @bug4388 normalize the project coltypes for union main select list + if (!csep->unionVec().empty()) + { + for (uint32_t i = 0; i < gwi.returnedCols.size(); i++) + { + vector coltypes; + + for (uint32_t j = 0; j < csep->unionVec().size(); j++) + { + coltypes.push_back( + dynamic_cast(csep->unionVec()[j].get())->returnedCols()[i]->resultType()); + + // @bug5976. set hasAggregate true for the main column if + // one corresponding union column has aggregate + if (dynamic_cast(csep->unionVec()[j].get())->returnedCols()[i]->hasAggregate()) + gwi.returnedCols[i]->hasAggregate(true); + } + + gwi.returnedCols[i]->resultType(dataconvert::DataConvert::convertUnionColType(coltypes)); + } + } + + // Having clause handling + gwi.clauseType = HAVING; + clearStacks(gwi); + ParseTree* havingFilter = 0; + // clear fatalParseError that may be left from post process functions + gwi.fatalParseError = false; + gwi.parseErrorText = ""; + + if (gi.groupByHaving != 0) + { + Item_cond* having = reinterpret_cast(gi.groupByHaving); +#ifdef DEBUG_WALK_COND + cerr << "------------------- HAVING ---------------------" << endl; + having->traverse_cond(debug_walk, &gwi, Item::POSTFIX); + cerr << "------------------------------------------------\n" << endl; +#endif + having->traverse_cond(gp_walk, &gwi, Item::POSTFIX); + + if (gwi.fatalParseError) + { + setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi); + return ER_INTERNAL_ERROR; + } + + ParseTree* ptp = 0; + ParseTree* rhs = 0; + + // @bug 4215. some function filter will be in the rcWorkStack. + if (gwi.ptWorkStack.empty() && !gwi.rcWorkStack.empty()) + { + havingFilter = new ParseTree(gwi.rcWorkStack.top()); + gwi.rcWorkStack.pop(); + } + + while (!gwi.ptWorkStack.empty()) + { + havingFilter = gwi.ptWorkStack.top(); + gwi.ptWorkStack.pop(); + + if (gwi.ptWorkStack.empty()) + break; + + ptp = new ParseTree(new LogicOperator("and")); + ptp->left(havingFilter); + rhs = gwi.ptWorkStack.top(); + gwi.ptWorkStack.pop(); + ptp->right(rhs); + gwi.ptWorkStack.push(ptp); + } + } + + // for post process expressions on the select list + // error out post process for union and sub select unit + if (isUnion || gwi.subSelectType != CalpontSelectExecutionPlan::MAIN_SELECT) + { + if (funcFieldVec.size() != 0 && !gwi.fatalParseError) + { + string emsg("Fatal parse error in vtable mode: Unsupported Items in union or sub select unit"); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg); + return ER_CHECK_NOT_IMPLEMENTED; + } + } + + for (uint32_t i = 0; i < funcFieldVec.size(); i++) + { + //SimpleColumn *sc = new SimpleColumn(funcFieldVec[i]->db_name, bestTableName(funcFieldVec[i])/*funcFieldVec[i]->table_name*/, funcFieldVec[i]->field_name, sessionID); + SimpleColumn* sc = buildSimpleColumn(funcFieldVec[i], gwi); + + if (!sc || gwi.fatalParseError) + { + string emsg; + + if (gwi.parseErrorText.empty()) + { + emsg = "un-recognized column"; + + if (funcFieldVec[i]->name) + emsg += string(funcFieldVec[i]->name); + } + else + { + emsg = gwi.parseErrorText; + } + + setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi); + return ER_INTERNAL_ERROR; + } + + String str; + funcFieldVec[i]->print(&str, QT_INFINIDB_NO_QUOTE); + sc->alias(string(str.c_ptr())); + //sc->tableAlias(funcFieldVec[i]->table_name); + sc->tableAlias(sc->tableAlias()); + SRCP srcp(sc); + uint32_t j = 0; + + for (; j < gwi.returnedCols.size(); j++) + { + if (sc->sameColumn(gwi.returnedCols[j].get())) + { + SimpleColumn* field = dynamic_cast(gwi.returnedCols[j].get()); + + if (field && field->alias() == sc->alias()) + break; + } + } + + if (j == gwi.returnedCols.size()) + { + gwi.returnedCols.push_back(srcp); + gwi.columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(string(funcFieldVec[i]->field_name), srcp)); + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + string fullname; + fullname = str.c_ptr(); + sel_cols_in_create += fullname + " `" + escapeBackTick(fullname.c_str()) + "`"; + TABLE_LIST* tmp = (funcFieldVec[i]->cached_table ? funcFieldVec[i]->cached_table : 0); + gwi.tableMap[make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias(), sc->isInfiniDB())] = + make_pair(1, tmp); + } + } + + // post-process Order by list and expressions on select by redo phase1. only for vtable + // ignore ORDER BY clause for union select unit + string ord_cols = ""; // for normal select phase + SRCP minSc; // min width projected column. for count(*) use + + // Group by list. not valid for union main query + + /* + if (gwi.thd->infinidb_vtable.vtable_state == THD::INFINIDB_CREATE_VTABLE && !unionSel) + { + gwi.clauseType = GROUP_BY; + Item* nonSupportItem = NULL; + ORDER* groupcol = reinterpret_cast(gi.groupByGr.first); + + // check if window functions are in order by. InfiniDB process order by list if + // window functions are involved, either in order by or projection. + bool hasWindowFunc = gwi.hasWindowFunc; + gwi.hasWindowFunc = false; + + for (; groupcol; groupcol = groupcol->next) + { + if ((*(groupcol->item))->type() == Item::WINDOW_FUNC_ITEM) + gwi.hasWindowFunc = true; + } + + if (gwi.hasWindowFunc) + { + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_WF_NOT_ALLOWED, "GROUP BY clause"); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + gwi.hasWindowFunc = hasWindowFunc; + groupcol = reinterpret_cast(gi.groupByGr.first); + + for (; groupcol; groupcol = groupcol->next) + { + Item* groupItem = *(groupcol->item); + + // @bug5993. Could be nested ref. + while (groupItem->type() == Item::REF_ITEM) + groupItem = (*((Item_ref*)groupItem)->ref); + + if (groupItem->type() == Item::FUNC_ITEM) + { + Item_func* ifp = (Item_func*)groupItem; + + // call buildFunctionColumn here mostly for finding out + // non-support column on GB list. Should be simplified. + ReturnedColumn* fc = buildFunctionColumn(ifp, gwi, gwi.fatalParseError); + + if (!fc || gwi.fatalParseError) + { + nonSupportItem = ifp; + break; + } + + if (groupcol->in_field_list && groupcol->counter_used) + { + delete fc; + fc = gwi.returnedCols[groupcol->counter - 1].get(); + SRCP srcp(fc->clone()); + + // check if no column parm + for (uint32_t i = 0; i < gwi.no_parm_func_list.size(); i++) + { + if (gwi.no_parm_func_list[i]->expressionId() == fc->expressionId()) + { + gwi.no_parm_func_list.push_back(dynamic_cast(srcp.get())); + break; + } + } + + srcp->orderPos(groupcol->counter - 1); + gwi.groupByCols.push_back(srcp); + continue; + } + else if (!groupItem->is_autogenerated_name) // alias + { + uint32_t i = 0; + + for (; i < gwi.returnedCols.size(); i++) + { + if (string(groupItem->name) == gwi.returnedCols[i]->alias()) + { + ReturnedColumn* rc = gwi.returnedCols[i]->clone(); + rc->orderPos(i); + gwi.groupByCols.push_back(SRCP(rc)); + delete fc; + break; + } + } + + if (i == gwi.returnedCols.size()) + { + nonSupportItem = groupItem; + break; + } + } + else + { + uint32_t i = 0; + + for (; i < gwi.returnedCols.size(); i++) + { + if (fc->operator==(gwi.returnedCols[i].get())) + { + ReturnedColumn* rc = gwi.returnedCols[i]->clone(); + rc->orderPos(i); + gwi.groupByCols.push_back(SRCP(rc)); + delete fc; + break; + } + } + + if (i == gwi.returnedCols.size()) + { + gwi.groupByCols.push_back(SRCP(fc)); + break; + } + } + } + else if (groupItem->type() == Item::FIELD_ITEM) + { + Item_field* ifp = (Item_field*)groupItem; + // this GB col could be an alias of F&E on the SELECT clause, not necessarily a field. + ReturnedColumn* rc = buildSimpleColumn(ifp, gwi); + SimpleColumn* sc = dynamic_cast(rc); + + for (uint32_t j = 0; j < gwi.returnedCols.size(); j++) + { + if (sc) + { + if (sc->sameColumn(gwi.returnedCols[j].get())) + { + sc->orderPos(j); + break; + } + else if (strcasecmp(sc->alias().c_str(), gwi.returnedCols[j]->alias().c_str()) == 0) + { + rc = gwi.returnedCols[j].get()->clone(); + rc->orderPos(j); + break; + } + } + else + { + if (ifp->name && string(ifp->name) == gwi.returnedCols[j].get()->alias()) + { + rc = gwi.returnedCols[j].get()->clone(); + rc->orderPos(j); + break; + } + } + } + + if (!rc) + { + nonSupportItem = ifp; + break; + } + + SRCP srcp(rc); + + // bug 3151 + AggregateColumn* ac = dynamic_cast(rc); + + if (ac) + { + nonSupportItem = ifp; + break; + } + + gwi.groupByCols.push_back(srcp); + gwi.columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name), srcp)); + } + // @bug5638. The group by column is constant but not counter, alias has to match a column + // on the select list + else if (!groupcol->counter_used && + (groupItem->type() == Item::INT_ITEM || + groupItem->type() == Item::STRING_ITEM || + groupItem->type() == Item::REAL_ITEM || + groupItem->type() == Item::DECIMAL_ITEM)) + { + ReturnedColumn* rc = 0; + + for (uint32_t j = 0; j < gwi.returnedCols.size(); j++) + { + if (groupItem->name && string(groupItem->name) == gwi.returnedCols[j].get()->alias()) + { + rc = gwi.returnedCols[j].get()->clone(); + rc->orderPos(j); + break; + } + } + + if (!rc) + { + nonSupportItem = groupItem; + break; + } + + gwi.groupByCols.push_back(SRCP(rc)); + } + else if ((*(groupcol->item))->type() == Item::SUBSELECT_ITEM) + { + if (!groupcol->in_field_list || !groupItem->name) + { + nonSupportItem = groupItem; + } + else + { + uint32_t i = 0; + + for (; i < gwi.returnedCols.size(); i++) + { + if (string(groupItem->name) == gwi.returnedCols[i]->alias()) + { + ReturnedColumn* rc = gwi.returnedCols[i]->clone(); + rc->orderPos(i); + gwi.groupByCols.push_back(SRCP(rc)); + break; + } + } + + if (i == gwi.returnedCols.size()) + { + nonSupportItem = groupItem; + } + } + } + // @bug 3761. + else if (groupcol->counter_used) + { + if (gwi.returnedCols.size() <= (uint32_t)(groupcol->counter - 1)) + { + nonSupportItem = groupItem; + } + else + { + gwi.groupByCols.push_back(SRCP(gwi.returnedCols[groupcol->counter - 1]->clone())); + } + } + else + { + nonSupportItem = groupItem; + } + + } + + // @bug 4756. Add internal groupby column for correlated join to the groupby list + if (gwi.aggOnSelect && !gwi.subGroupByCols.empty()) + gwi.groupByCols.insert(gwi.groupByCols.end(), gwi.subGroupByCols.begin(), gwi.subGroupByCols.end()); + + // this is window func on SELECT becuase ORDER BY has not been processed + if (!gwi.windowFuncList.empty() && !gwi.subGroupByCols.empty()) + { + for (uint32_t i = 0; i < gwi.windowFuncList.size(); i++) + { + if (gwi.windowFuncList[i]->hasWindowFunc()) + { + vector windowFunctions = gwi.windowFuncList[i]->windowfunctionColumnList(); + + for (uint32_t j = 0; j < windowFunctions.size(); j++) + windowFunctions[j]->addToPartition(gwi.subGroupByCols); + } + } + } + + if (nonSupportItem) + { + Message::Args args; + + if (nonSupportItem->name) + args.add("'" + string(nonSupportItem->name) + "'"); + else + args.add(""); + + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_GROUP_BY, args); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + } + */ + + + if (gwi.thd->infinidb_vtable.vtable_state == THD::INFINIDB_CREATE_VTABLE) + { + //SQL_I_List order_list = gi.groupByOrder; + ORDER* ordercol = reinterpret_cast(gi.groupByOrder); + string create_query(gwi.thd->infinidb_vtable.create_vtable_query.c_ptr()); + string select_query(gwi.thd->infinidb_vtable.select_vtable_query.c_ptr()); + string lower_create_query(gwi.thd->infinidb_vtable.create_vtable_query.c_ptr()); + string lower_select_query(gwi.thd->infinidb_vtable.select_vtable_query.c_ptr()); + algorithm::to_lower(lower_create_query); + algorithm::to_lower(lower_select_query); + + + // check if window functions are in order by. InfiniDB process order by list if + // window functions are involved, either in order by or projection. + for (; ordercol; ordercol = ordercol->next) + { + if ((*(ordercol->item))->type() == Item::WINDOW_FUNC_ITEM) + gwi.hasWindowFunc = true; + } + + // re-visit the first of ordercol list + ordercol = reinterpret_cast(gi.groupByOrder); + + // for subquery, order+limit by will be supported in infinidb. build order by columns + // @todo union order by and limit support + if (gwi.hasWindowFunc || gwi.subSelectType != CalpontSelectExecutionPlan::MAIN_SELECT) + { + for (; ordercol; ordercol = ordercol->next) + { + ReturnedColumn* rc = NULL; + + if (ordercol->in_field_list && ordercol->counter_used) + { + rc = gwi.returnedCols[ordercol->counter - 1]->clone(); + rc->orderPos(ordercol->counter - 1); + // can not be optimized off if used in order by with counter. + // set with self derived table alias if it's derived table + gwi.returnedCols[ordercol->counter - 1]->incRefCount(); + } + else + { + Item* ord_item = *(ordercol->item); + + // ignore not_used column on order by. + if (ord_item->type() == Item::INT_ITEM && ord_item->full_name() && string(ord_item->full_name()) == "Not_used") + continue; + else if (ord_item->type() == Item::INT_ITEM) + rc = gwi.returnedCols[((Item_int*)ord_item)->val_int() - 1]->clone(); + else if (ord_item->type() == Item::SUBSELECT_ITEM) + gwi.fatalParseError = true; + else + rc = buildReturnedColumn(ord_item, gwi, gwi.fatalParseError); + + // @bug5501 try item_ptr if item can not be fixed. For some + // weird dml statement state, item can not be fixed but the + // infomation is available in item_ptr. + if (!rc || gwi.fatalParseError) + { + Item* item_ptr = ordercol->item_ptr; + + while (item_ptr->type() == Item::REF_ITEM) + item_ptr = *(((Item_ref*)item_ptr)->ref); + + rc = buildReturnedColumn(item_ptr, gwi, gwi.fatalParseError); + } + + if (!rc) + { + string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY); + gwi.parseErrorText = emsg; + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + } + + if (ordercol->direction == ORDER::ORDER_ASC) + rc->asc(true); + else + rc->asc(false); + + gwi.orderByCols.push_back(SRCP(rc)); + } + } + else if (!isUnion) + { + vector fieldVec; + bool addToSel; + + // the following order by is just for redo phase + if (!unionSel) + { + for (; ordercol; ordercol = ordercol->next) + { + Item* ord_item = *(ordercol->item); + + // @bug5993. Could be nested ref. + while (ord_item->type() == Item::REF_ITEM) + ord_item = (*((Item_ref*)ord_item)->ref); + + // @bug 1706. re-construct the order by item one by one + //Item* ord_item = *(ordercol->item); + if (ord_cols.length() != 0) + ord_cols += ", "; + + addToSel = true; + string fullname; + + if (ordercol->in_field_list && ordercol->counter_used) + { + ostringstream oss; + oss << ordercol->counter; + ord_cols += oss.str(); + + if (ordercol->direction != ORDER::ORDER_ASC) + ord_cols += " desc"; + + continue; + } + + else if (ord_item->type() == Item::FUNC_ITEM) + { + // @bug 2621. order by alias + if (!ord_item->is_autogenerated_name && ord_item->name) + { + ord_cols += ord_item->name; + continue; + } + + // if there's group by clause or aggregate column, check to see + // if this item or the arguments is on the GB list. + ReturnedColumn* rc = 0; + // check if this order by column is on the select list + Item_func* ifp = (Item_func*)(*(ordercol->item)); + rc = buildFunctionColumn(ifp, gwi, gwi.fatalParseError); + + if (rc) + { + for (uint32_t i = 0; i < gwi.returnedCols.size(); i++) + { + if (rc && rc->operator==(gwi.returnedCols[i].get())) + { + ostringstream oss; + oss << i + 1; + ord_cols += oss.str(); + addToSel = false; + break; + } + } + } + + if (addToSel) + { + FunctionColumn* fc = dynamic_cast(rc); + + if (fc) + { + addToSel = false; + redo = true; + string ord_func = string(ifp->func_name()) + "("; + + for (uint32_t i = 0; i < fc->functionParms().size(); i++) + { + if (i != 0) + ord_func += ","; + + for (uint32_t j = 0; j < gwi.returnedCols.size(); j++) + { + if (fc->functionParms()[i]->data()->operator==(gwi.returnedCols[j].get())) + { + ord_func += "`" + escapeBackTick(gwi.returnedCols[j]->alias().c_str()) + "`"; + continue; + } + + AggregateColumn* ac = dynamic_cast(fc->functionParms()[i]->data()); + + if (ac) + { + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + addToSel = true; + //continue; + + } + } + + ord_func += ")"; + + if (!addToSel) + ord_cols += ord_func; + } + } + } + else if (ord_item->type() == Item::SUBSELECT_ITEM) + { + string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + else if (ord_item->type() == Item::SUM_FUNC_ITEM) + { + ReturnedColumn* ac = 0; + + Item_sum* ifp = (Item_sum*)(*(ordercol->item)); + // @bug3477. add aggregate column to the select list of the create phase. + ac = buildAggregateColumn(ifp, gwi); + + if (!ac) + { + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, + IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY), gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + // check if this order by column is on the select list + for (uint32_t i = 0; i < gwi.returnedCols.size(); i++) + { + AggregateColumn* ret = dynamic_cast(gwi.returnedCols[i].get()); + + if (!ret) + continue; + + if (ac->operator==(gwi.returnedCols[i].get())) + { + ostringstream oss; + oss << i + 1; + ord_cols += oss.str(); + addToSel = false; + break; + } + } + + if (ac || !gwi.groupByCols.empty()) + { + if (addToSel) + { + redo = true; + // @bug 3076. do not add the argument of aggregate function to the SELECT list, + // instead, add the whole column + String str; + ord_item->print(&str, QT_INFINIDB_NO_QUOTE); + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + sel_cols_in_create += str.c_ptr(); + //gwi.selectCols.push_back(" `" + string(str.c_ptr()) + "`"); + SRCP srcp(ac); + gwi.returnedCols.push_back(srcp); + ord_cols += " `" + escapeBackTick(str.c_ptr()) + "`"; + } + + if (ordercol->direction != ORDER::ORDER_ASC) + ord_cols += " desc"; + + continue; + } + } + else if (ord_item->name && ord_item->type() == Item::FIELD_ITEM) + { + Item_field* field = reinterpret_cast(ord_item); + ReturnedColumn* rc = buildSimpleColumn(field, gwi); + fullname = field->full_name(); +// if (field->db_name) +// fullname += string(field->db_name) + "."; +// if (field->table_name) +// fullname += string(field->table_name) + "."; +// if (field->field_name) +// fullname += string(field->field_name); + + for (uint32_t i = 0; i < gwi.returnedCols.size(); i++) + { + SimpleColumn* sc = dynamic_cast(gwi.returnedCols[i].get()); + + if (sc && ((Item_field*)ord_item)->cached_table && + (strcasecmp(getViewName(((Item_field*)ord_item)->cached_table).c_str(), sc->viewName().c_str()) != 0)) + { + continue; + } + + if (strcasecmp(fullname.c_str(), gwi.returnedCols[i]->alias().c_str()) == 0 || + strcasecmp(ord_item->name, gwi.returnedCols[i]->alias().c_str()) == 0) + { + ord_cols += string(" `") + escapeBackTick(gwi.returnedCols[i]->alias().c_str()) + '`'; + addToSel = false; + break; + } + + if (sc && sc->sameColumn(rc)) + { + ostringstream oss; + oss << i + 1; + ord_cols += oss.str(); + addToSel = false; + break; + } + } + } + + if (addToSel) + { + // @bug 2719. Error out order by not on the distinct select list. + if ( gi.groupByDistinct ) + { + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_ORDERBY_NOT_IN_DISTINCT); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + bool hasNonSupportItem = false; + uint16_t parseInfo = 0; + parse_item(ord_item, fieldVec, hasNonSupportItem, parseInfo); + + if (hasNonSupportItem) + { + string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + String str; + ord_item->print(&str, QT_INFINIDB); + ord_cols += str.c_ptr(); + } + + if (ordercol->direction != ORDER::ORDER_ASC) + ord_cols += " desc"; + } + } + + redo = (redo || fieldVec.size() != 0); + + // populate string to be added to the select list for order by + for (uint32_t i = 0; i < fieldVec.size(); i++) + { + SimpleColumn* sc = buildSimpleColumn(fieldVec[i], gwi); + + if (!sc) + { + string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + String str; + fieldVec[i]->print(&str, QT_INFINIDB_NO_QUOTE); + sc->alias(string(str.c_ptr())); + SRCP srcp(sc); + uint32_t j = 0; + + for (; j < gwi.returnedCols.size(); j++) + { + if (sc->sameColumn(gwi.returnedCols[j].get())) + { + SimpleColumn* field = dynamic_cast(gwi.returnedCols[j].get()); + + if (field && field->alias() == sc->alias()) + break; + } + } + + if (j == gwi.returnedCols.size()) + { + string fullname; + + if (sel_cols_in_create.length() != 0) + sel_cols_in_create += ", "; + + fullname = str.c_ptr(); + sel_cols_in_create += fullname + " `" + escapeBackTick(fullname.c_str()) + "`"; + + gwi.returnedCols.push_back(srcp); + gwi.columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(string(fieldVec[i]->field_name), srcp)); + TABLE_LIST* tmp = (fieldVec[i]->cached_table ? fieldVec[i]->cached_table : 0); + gwi.tableMap[make_aliastable(sc->schemaName(), sc->tableName(), sc->tableAlias(), sc->isInfiniDB())] = + make_pair(1, tmp); + } + } + } + + // make sure columnmap, returnedcols and count(*) arg_list are not empty + TableMap::iterator tb_iter = gwi.tableMap.begin(); + + try + { + for (; tb_iter != gwi.tableMap.end(); tb_iter++) + { + if ((*tb_iter).second.first == 1) continue; + + CalpontSystemCatalog::TableAliasName tan = (*tb_iter).first; + CalpontSystemCatalog::TableName tn = make_table((*tb_iter).first.schema, (*tb_iter).first.table); + SimpleColumn* sc = getSmallestColumn(csc, tn, tan, (*tb_iter).second.second->table, gwi); + SRCP srcp(sc); + gwi.columnMap.insert(CalpontSelectExecutionPlan::ColumnMap::value_type(sc->columnName(), srcp)); + (*tb_iter).second.first = 1; + } + } + catch (runtime_error& e) + { + setError(gwi.thd, ER_INTERNAL_ERROR, e.what(), gwi); + CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID); + return ER_INTERNAL_ERROR; + } + catch (...) + { + string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR); + setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi); + CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID); + return ER_INTERNAL_ERROR; + } + + if (!gwi.count_asterisk_list.empty() || !gwi.no_parm_func_list.empty() || + gwi.returnedCols.empty()) + { + // get the smallest column from colmap + CalpontSelectExecutionPlan::ColumnMap::const_iterator iter; + int minColWidth = 0; + CalpontSystemCatalog::ColType ct; + + try + { + for (iter = gwi.columnMap.begin(); iter != gwi.columnMap.end(); ++iter) + { + // should always not null + SimpleColumn* sc = dynamic_cast(iter->second.get()); + + if (sc && !(sc->joinInfo() & JOIN_CORRELATED)) + { + ct = csc->colType(sc->oid()); + + if (minColWidth == 0) + { + minColWidth = ct.colWidth; + minSc = iter->second; + } + else if (ct.colWidth < minColWidth) + { + minColWidth = ct.colWidth; + minSc = iter->second; + } + } + } + } + catch (...) + { + string emsg = IDBErrorInfo::instance()->errorMsg(ERR_LOST_CONN_EXEMGR); + setError(gwi.thd, ER_INTERNAL_ERROR, emsg, gwi); + CalpontSystemCatalog::removeCalpontSystemCatalog(sessionID); + return ER_INTERNAL_ERROR; + } + + if (gwi.returnedCols.empty() && gwi.additionalRetCols.empty()) + gwi.returnedCols.push_back(minSc); + } + + if (!isUnion && !gwi.hasWindowFunc && gwi.subSelectType == CalpontSelectExecutionPlan::MAIN_SELECT) + { + std::ostringstream vtb; + vtb << "infinidb_vtable.$vtable_" << gwi.thd->thread_id; + + //vtb << "$vtable_" << gwi.thd->thread_id; + // re-construct the select query and redo phase 1 + if (redo) + { + // select now() from region case. returnedCols should have minSc. + if (sel_cols_in_create.length() == 0) + { + SimpleColumn* sc = dynamic_cast(gwi.returnedCols[0].get()); + + if (sc) + sel_cols_in_create = dynamic_cast(gwi.returnedCols[0].get())->columnName(); + else + sel_cols_in_create = gwi.returnedCols[0]->alias(); + } + + // select * from derived table case + if (gwi.selectCols.empty()) + sel_cols_in_create = " * "; + + create_query = "create temporary table " + vtb.str() + " engine = aria as select " + sel_cols_in_create + " from "; + TABLE_LIST* table_ptr = gi.groupByTables; + + bool firstTb = true; + + // put all tables, derived tables and views on the list + //TABLE_LIST* table_ptr = select_lex.get_table_list(); + set aliasSet; // to avoid duplicate table alias + + for (; table_ptr; table_ptr = table_ptr->next_global) + { + if (string(table_ptr->table_name).find("$vtable") != string::npos) + continue; + + if (table_ptr->derived) + { + if (aliasSet.find(table_ptr->alias) != aliasSet.end()) + continue; + + String str; + (table_ptr->derived->first_select())->print(gwi.thd, &str, QT_INFINIDB_DERIVED); + + if (!firstTb) + create_query += ", "; + + create_query += "(" + string(str.c_ptr()) + ") " + string(table_ptr->alias); + firstTb = false; + aliasSet.insert(table_ptr->alias); + } + else if (table_ptr->view) + { + if (aliasSet.find(table_ptr->alias) != aliasSet.end()) + continue; + + if (!firstTb) + create_query += ", "; + + create_query += string(table_ptr->db) + "." + string(table_ptr->table_name) + + string(" `") + escapeBackTick(table_ptr->alias) + string("`"); + aliasSet.insert(table_ptr->alias); + firstTb = false; + } + else + { + // table referenced by view is represented by viewAlias_tableAlias. + // consistent with item.cc field print. + if (table_ptr->referencing_view) + { + if (aliasSet.find(string(table_ptr->referencing_view->alias) + "_" + + string(table_ptr->alias)) != aliasSet.end()) + continue; + + if (!firstTb) + create_query += ", "; + + create_query += string(table_ptr->db) + "." + string(table_ptr->table_name) + string(" "); + create_query += string(" `") + + escapeBackTick(table_ptr->referencing_view->alias) + "_" + + escapeBackTick(table_ptr->alias) + string("`"); + aliasSet.insert(string(table_ptr->referencing_view->alias) + "_" + + string(table_ptr->alias)); + } + else + { + if (aliasSet.find(table_ptr->alias) != aliasSet.end()) + continue; + + if (!firstTb) + create_query += ", "; + + create_query += string(table_ptr->db) + "." + string(table_ptr->table_name) + string(" "); + create_query += string("`") + escapeBackTick(table_ptr->alias) + string("`"); + aliasSet.insert(table_ptr->alias); + } + + firstTb = false; + } + } + + + gwi.thd->infinidb_vtable.create_vtable_query.free(); + gwi.thd->infinidb_vtable.create_vtable_query.append(create_query.c_str(), create_query.length()); + gwi.thd->infinidb_vtable.vtable_state = THD::INFINIDB_REDO_PHASE1; // redo phase 1 + + // turn off select distinct from post process unless there're post process functions + // on the select list. + string sel_query = "select "; + + if (gi.groupByDistinct && redo) + sel_query = "select distinct "; + else + sel_query = "select "; + + // select * from derived table... + if (gwi.selectCols.size() == 0) + sel_query += " * "; + + for (uint32_t i = 0; i < gwi.selectCols.size(); i++) + { + sel_query += gwi.selectCols[i]; + + if ( i + 1 != gwi.selectCols.size()) + sel_query += ", "; + } + + select_query.replace(lower_select_query.find("select *"), string("select *").length(), sel_query); + } + else + { + // remove order by clause in case this phase has been executed before. + // need a better fix later, like skip all the other non-optimized phase. + size_t pos = lower_select_query.find("order by"); + + if (pos != string::npos) + select_query.replace(pos, lower_select_query.length() - pos, ""); + + //select_query = "select * from " + vtb.str(); + // MCOL-1052 + if (unionSel) + { + ordercol = reinterpret_cast(gi.groupByOrder); + //order_list = gi.groupByOrder; + } + else + ordercol = 0; + + ord_cols = ""; + + for (; ordercol; ordercol = ordercol->next) + { + Item* ord_item = *(ordercol->item); + + // @bug 1706. re-construct the order by item one by one, because the ord_cols constucted so far + // is for REDO phase. + if (ord_cols.length() != 0) + ord_cols += ", "; + + if (ordercol->in_field_list && ordercol->counter_used) + { + ostringstream oss; + oss << ordercol->counter; + ord_cols += oss.str(); + } + else if (ord_item->type() == Item::NULL_ITEM) + { + // MCOL-793 Do nothing for an ORDER BY NULL + } + else if (ord_item->type() == Item::SUM_FUNC_ITEM) + { + Item_sum* ifp = (Item_sum*)(*(ordercol->item)); + ReturnedColumn* fc = buildAggregateColumn(ifp, gwi); + + for (uint32_t i = 0; i < gwi.returnedCols.size(); i++) + { + if (fc->operator==(gwi.returnedCols[i].get())) + { + ostringstream oss; + oss << i + 1; + ord_cols += oss.str(); + break; + } + } + + //continue; + } + // @bug 3518. if order by clause = selected column, use position. + else if (ord_item->name && ord_item->type() == Item::FIELD_ITEM) + { + Item_field* field = reinterpret_cast(ord_item); + string fullname; + + if (field->db_name) + fullname += string(field->db_name) + "."; + + if (field->table_name) + fullname += string(field->table_name) + "."; + + if (field->field_name) + fullname += string(field->field_name); + + uint32_t i = 0; + + for (i = 0; i < gwi.returnedCols.size(); i++) + { + SimpleColumn* sc = dynamic_cast(gwi.returnedCols[i].get()); + + if (sc && ((Item_field*)ord_item)->cached_table && + (strcasecmp(getViewName(((Item_field*)ord_item)->cached_table).c_str(), sc->viewName().c_str()) != 0)) + continue; + + if (strcasecmp(fullname.c_str(), gwi.returnedCols[i]->alias().c_str()) == 0 || + strcasecmp(ord_item->name, gwi.returnedCols[i]->alias().c_str()) == 0) + { + ostringstream oss; + oss << i + 1; + ord_cols += oss.str(); + break; + } + } + + if (i == gwi.returnedCols.size()) + ord_cols += string(" `") + escapeBackTick(ord_item->name) + '`'; + } + + else if (ord_item->name) + { + // for union order by 1 case. For unknown reason, it doesn't show in_field_list + if (ord_item->type() == Item::INT_ITEM) + { + ord_cols += ord_item->name; + } + else if (ord_item->type() == Item::SUBSELECT_ITEM) + { + string emsg = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_ORDER_BY); + setError(gwi.thd, ER_CHECK_NOT_IMPLEMENTED, emsg, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + else + { + ord_cols += string(" `") + escapeBackTick(ord_item->name) + '`'; + } + } + else if (ord_item->type() == Item::FUNC_ITEM) + { + // @bug5636. check if this order by column is on the select list + ReturnedColumn* rc = buildFunctionColumn((Item_func*)(ord_item), gwi, gwi.fatalParseError); + + for (uint32_t i = 0; i < gwi.returnedCols.size(); i++) + { + if (rc && rc->operator==(gwi.returnedCols[i].get())) + { + ostringstream oss; + oss << i + 1; + ord_cols += oss.str(); + break; + } + } + } + else + { + String str; + ord_item->print(&str, QT_INFINIDB_NO_QUOTE); + ord_cols += string(str.c_ptr()); + } + + if (ordercol->direction != ORDER::ORDER_ASC) + ord_cols += " desc"; + } + } + + if (ord_cols.length() > 0) // has order by + { + gwi.thd->infinidb_vtable.has_order_by = true; + csep->hasOrderBy(true); + ord_cols = " order by " + ord_cols; + select_query += ord_cols; + } + } + +// MCOL-1052 LIMIT +/* + if (unionSel || gwi.subSelectType != CalpontSelectExecutionPlan::MAIN_SELECT) + { + if (select_lex.master_unit()->global_parameters()->explicit_limit) + { + if (select_lex.master_unit()->global_parameters()->offset_limit) + { + Item_int* offset = (Item_int*)select_lex.master_unit()->global_parameters()->offset_limit; + csep->limitStart(offset->val_int()); + } + + if (select_lex.master_unit()->global_parameters()->select_limit) + { + Item_int* select = (Item_int*)select_lex.master_unit()->global_parameters()->select_limit; + csep->limitNum(select->val_int()); + } + + if (unionSel && gwi.subSelectType == CalpontSelectExecutionPlan::MAIN_SELECT) + { + ostringstream limit; + limit << " limit "; + limit << csep->limitStart() << ", "; + limit << csep->limitNum(); + select_query += limit.str(); + } + } + } + else if (isUnion && select_lex.explicit_limit) + { + if (select_lex.braces) + { + if (select_lex.offset_limit) + csep->limitStart(((Item_int*)select_lex.offset_limit)->val_int()); + + if (select_lex.select_limit) + csep->limitNum(((Item_int*)select_lex.select_limit)->val_int()); + } + } + else if (select_lex.explicit_limit) + { + uint32_t limitOffset = 0; + uint32_t limitNum = std::numeric_limits::max(); + + if (join) + { +#if MYSQL_VERSION_ID >= 50172 + + // @bug5729. After upgrade, join->unit sometimes is uninitialized pointer + // (not null though) and will cause seg fault. Prefer checking + // select_lex->offset_limit if not null. + if (join->select_lex && + join->select_lex->offset_limit && + join->select_lex->offset_limit->fixed && + join->select_lex->select_limit && + join->select_lex->select_limit->fixed) + { + limitOffset = join->select_lex->offset_limit->val_int(); + limitNum = join->select_lex->select_limit->val_int(); + } + + else if (join->unit) + { + limitOffset = join->unit->offset_limit_cnt; + limitNum = join->unit->select_limit_cnt - limitOffset; + } + +#else + limitOffset = (join->unit)->offset_limit_cnt; + limitNum = (join->unit)->select_limit_cnt - (join->unit)->offset_limit_cnt; +#endif + } + else + { + if (select_lex.master_unit()->global_parameters()->offset_limit) + { + Item_int* offset = (Item_int*)select_lex.master_unit()->global_parameters()->offset_limit; + limitOffset = offset->val_int(); + } + + if (select_lex.master_unit()->global_parameters()->select_limit) + { + Item_int* select = (Item_int*)select_lex.master_unit()->global_parameters()->select_limit; + limitNum = select->val_int(); + } + } + + // relate to bug4848. let mysql drive limit when limit session variable set. + // do not set in csep. @bug5096. ignore session limit setting for dml + if ((gwi.thd->variables.select_limit == (uint64_t) - 1 || + (gwi.thd->variables.select_limit != (uint64_t) - 1 && + gwi.thd->infinidb_vtable.vtable_state != THD::INFINIDB_CREATE_VTABLE)) && + !csep->hasOrderBy()) + { + csep->limitStart(limitOffset); + csep->limitNum(limitNum); + } + else + { + ostringstream limit; + limit << " limit " << limitOffset << ", " << limitNum; + select_query += limit.str(); + } + } + gwi.thd->infinidb_vtable.select_vtable_query.free(); + gwi.thd->infinidb_vtable.select_vtable_query.append(select_query.c_str(), select_query.length()); + + // We don't currently support limit with correlated subquery + if (csep->limitNum() != (uint64_t) - 1 && + gwi.subQuery && !gwi.correlatedTbNameVec.empty()) + { + gwi.fatalParseError = true; + gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_LIMIT_SUB); + setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + */ + } + + + + if ( gi.groupByDistinct ) + csep->distinct(true); + + // add the smallest column to count(*) parm. + // select constant in subquery case + std::vector::iterator coliter; + + if (!minSc) + { + if (!gwi.returnedCols.empty()) + minSc = gwi.returnedCols[0]; + else if (!gwi.additionalRetCols.empty()) + minSc = gwi.additionalRetCols[0]; + } + + // @bug3523, count(*) on subquery always pick column[0]. + SimpleColumn* sc = dynamic_cast(minSc.get()); + + if (sc && sc->schemaName().empty()) + { + if (gwi.derivedTbList.size() >= 1) + { + SimpleColumn* sc1 = new SimpleColumn(); + sc1->columnName(sc->columnName()); + sc1->tableName(sc->tableName()); + sc1->tableAlias(sc->tableAlias()); + sc1->viewName(lower(sc->viewName())); + sc1->colPosition(0); + minSc.reset(sc1); + } + } + + for (coliter = gwi.count_asterisk_list.begin(); coliter != gwi.count_asterisk_list.end(); ++coliter) + { + // @bug5977 @note should never throw this, but checking just in case. + // When ExeMgr fix is ready, this should not error out... + if (dynamic_cast(minSc.get())) + { + gwi.fatalParseError = true; + gwi.parseErrorText = "No project column found for aggregate function"; + setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText, gwi); + return ER_CHECK_NOT_IMPLEMENTED; + } + + (*coliter)->functionParms(minSc); + } + + std::vector::iterator funciter; + + SPTP sptp(new ParseTree(minSc.get()->clone())); + + for (funciter = gwi.no_parm_func_list.begin(); funciter != gwi.no_parm_func_list.end(); ++funciter) + { + FunctionParm funcParms = (*funciter)->functionParms(); + funcParms.push_back(sptp); + (*funciter)->functionParms(funcParms); + } + + // set sequence# for subquery localCols + for (uint32_t i = 0; i < gwi.localCols.size(); i++) + gwi.localCols[i]->sequence(i); + + // append additionalRetCols to returnedCols + gwi.returnedCols.insert(gwi.returnedCols.begin(), gwi.additionalRetCols.begin(), + gwi.additionalRetCols.end()); + + csep->groupByCols(gwi.groupByCols); + csep->orderByCols(gwi.orderByCols); + csep->returnedCols(gwi.returnedCols); + csep->columnMap(gwi.columnMap); + csep->having(havingFilter); + csep->derivedTableList(gwi.derivedTbList); + csep->selectSubList(selectSubList); + csep->subSelectList(gwi.subselectList); + gwi.thd->infinidb_vtable.duplicate_field_name = false; + clearStacks(gwi); + return 0; +} + } diff --git a/dbcon/mysql/ha_calpont_impl.cpp b/dbcon/mysql/ha_calpont_impl.cpp index 5d8e91e08..c8a02713a 100644 --- a/dbcon/mysql/ha_calpont_impl.cpp +++ b/dbcon/mysql/ha_calpont_impl.cpp @@ -5532,6 +5532,7 @@ int ha_calpont_impl_group_by_init(ha_calpont_group_by_handler* group_hand, TABLE sm::tableid_t tableid = 0; cal_table_info ti; + cal_group_info gi; sm::cpsm_conhdl_t* hndl; SCSEP csep; @@ -5602,10 +5603,19 @@ int ha_calpont_impl_group_by_init(ha_calpont_group_by_handler* group_hand, TABLE ti.csep->traceFlags(ci->traceFlags); ti.msTablePtr = group_hand->table_list->table; - ti.groupByFields = group_hand->fields; + //ti.groupByTables = group_hand->table_list; + //ti.groupByFields = group_hand->fields; + gi.groupByTables = group_hand->table_list; + gi.groupByFields = group_hand->select; + gi.groupByWhere = group_hand->where; + gi.groupByGroup = group_hand->group_by; + gi.groupByOrder = group_hand->order_by; + gi.groupByHaving = group_hand->having; + gi.groupByDistinct = group_hand->distinct; + // send plan whenever group_init is called - cp_get_group_plan(thd, ti.csep, ti); + cp_get_group_plan(thd, ti.csep, ti, gi); } IDEBUG( cerr << tableName << " send plan:" << endl ); diff --git a/dbcon/mysql/ha_calpont_impl_if.h b/dbcon/mysql/ha_calpont_impl_if.h index 71fd0b35f..7c12c8b2c 100644 --- a/dbcon/mysql/ha_calpont_impl_if.h +++ b/dbcon/mysql/ha_calpont_impl_if.h @@ -178,8 +178,7 @@ struct cal_table_info msTablePtr(0), conn_hndl(0), condInfo(0), - moreRows(false), - groupByFields(0) + moreRows(false) { } ~cal_table_info() {} sm::cpsm_tplh_t* tpl_ctx; @@ -190,7 +189,27 @@ struct cal_table_info gp_walk_info* condInfo; execplan::SCSEP csep; bool moreRows; //are there more rows to consume (b/c of limit) - List *groupByFields; // MCOL-1052 For CSEP generation +}; + +struct cal_group_info +{ + cal_group_info() : groupByFields(0), + groupByTables(0), + groupByWhere(0), + groupByGroup(0), + groupByOrder(0), + groupByHaving(0), + groupByDistinct(false) + { } + ~cal_group_info() { } + + List* groupByFields; // MCOL-1052 SELECT + TABLE_LIST* groupByTables; // MCOL-1052 FROM + Item* groupByWhere; // MCOL-1052 WHERE + ORDER* groupByGroup; // MCOL-1052 GROUP BY + ORDER* groupByOrder; // MCOL-1052 ORDER BY + Item* groupByHaving; // MCOL-1052 HAVING + bool groupByDistinct; //MCOL-1052 DISTINCT }; typedef std::tr1::unordered_map CalTableMap; @@ -299,8 +318,9 @@ const std::string infinidb_err_msg = "\nThe query includes syntax that is not su int cp_get_plan(THD* thd, execplan::SCSEP& csep); int cp_get_table_plan(THD* thd, execplan::SCSEP& csep, cal_impl_if::cal_table_info& ti); -int cp_get_group_plan(THD* thd, execplan::SCSEP& csep, cal_impl_if::cal_table_info& ti); +int cp_get_group_plan(THD* thd, execplan::SCSEP& csep, cal_impl_if::cal_table_info& ti,cal_impl_if::cal_group_info& gi); int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, execplan::SCSEP& csep, bool isUnion = false); +int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, execplan::SCSEP& csep,cal_group_info& gi, bool isUnion = false); void setError(THD* thd, uint32_t errcode, const std::string errmsg, gp_walk_info* gwi); void setError(THD* thd, uint32_t errcode, const std::string errmsg); void gp_walk(const Item* item, void* arg);