diff --git a/dbcon/mysql/CMakeLists.txt b/dbcon/mysql/CMakeLists.txt index cb176f371..711c0a188 100644 --- a/dbcon/mysql/CMakeLists.txt +++ b/dbcon/mysql/CMakeLists.txt @@ -23,6 +23,8 @@ set(libcalmysql_SRCS ha_mcs_impl.cpp ha_mcs_dml.cpp ha_mcs_ddl.cpp + ha_mcs_execplan_helpers.cpp + ha_mcs_execplan_walks.cpp ha_mcs_execplan.cpp ha_scalar_sub.cpp ha_in_sub.cpp diff --git a/dbcon/mysql/ha_mcs_execplan.cpp b/dbcon/mysql/ha_mcs_execplan.cpp index dcbcdcfd8..6e19dda44 100644 --- a/dbcon/mysql/ha_mcs_execplan.cpp +++ b/dbcon/mysql/ha_mcs_execplan.cpp @@ -54,6 +54,9 @@ using namespace logging; #include "mcsv1_udaf.h" +#include "ha_mcs_execplan_walks.h" +#include "ha_mcs_execplan_parseinfo_bits.h" +#include "ha_mcs_execplan_helpers.h" #include "ha_mcs_impl_if.h" #include "ha_mcs_sysvars.h" #include "ha_subquery.h" @@ -93,50 +96,6 @@ using namespace funcexp; #include "vlarray.h" -const uint64_t AGG_BIT = 0x01; -const uint64_t SUB_BIT = 0x02; -const uint64_t AF_BIT = 0x04; -const uint64_t CORRELATED = 0x08; - -// In certain cases, gp_walk is called recursively. When done so, -// we need to bookmark the rcWorkStack for those cases where a constant -// expression such as 1=1 is used in an if statement or function call. -// This is a seriously bad kludge for MariaDB bug 750. -// -// BM => BookMark -// HWM => HighWaterMark -class RecursionCounter -{ - private: - RecursionCounter() - { - } - - public: - RecursionCounter(gp_walk_info* gwip) : fgwip(gwip) - { - ++fgwip->recursionLevel; - - if (fgwip->recursionLevel > fgwip->recursionHWM) - { - fgwip->rcBookMarkStack.push(fgwip->rcWorkStack.size()); - fgwip->recursionHWM = fgwip->recursionLevel; - } - } - ~RecursionCounter() - { - --fgwip->recursionLevel; - - if (fgwip->recursionLevel < fgwip->recursionHWM - 1) - { - fgwip->rcBookMarkStack.pop(); - --fgwip->recursionHWM; - } - } - - gp_walk_info* fgwip; -}; - #include "ha_view.h" namespace cal_impl_if @@ -455,21 +414,6 @@ void clearDeleteStacks(gp_walk_info& gwi) gwi.viewList.clear(); } -bool nonConstFunc(Item_func* ifp) -{ - if (strcasecmp(ifp->func_name(), "rand") == 0 || strcasecmp(ifp->func_name(), "sysdate") == 0 || - strcasecmp(ifp->func_name(), "idblocalpm") == 0) - return true; - - for (uint32_t i = 0; i < ifp->argument_count(); i++) - { - if (ifp->arguments()[i]->type() == Item::FUNC_ITEM && nonConstFunc(((Item_func*)ifp->arguments()[i]))) - return true; - } - - return false; -} - /*@brief getColNameFromItem - builds a name from an Item */ /*********************************************************** * DESCRIPTION: @@ -810,659 +754,6 @@ string getViewName(TABLE_LIST* table_ptr) return viewName; } -#ifdef DEBUG_WALK_COND -void debug_walk(const Item* item, void* arg) -{ - switch (item->type()) - { - case Item::FIELD_ITEM: - { - Item_field* ifp = (Item_field*)item; - cerr << "FIELD_ITEM: " << (ifp->db_name.str ? ifp->db_name.str : "") << '.' << bestTableName(ifp) << '.' - << ifp->field_name.str << endl; - break; - } - case Item::CONST_ITEM: - { - switch (item->cmp_type()) - { - case INT_RESULT: - { - Item_int* iip = (Item_int*)item; - cerr << "INT_ITEM: "; - - if (iip->name.length) - cerr << iip->name.str << " (from name string)" << endl; - else - cerr << iip->val_int() << endl; - - break; - } - case STRING_RESULT: - { - Item_string* isp = (Item_string*)item; - String val, *str = isp->val_str(&val); - string valStr; - valStr.assign(str->ptr(), str->length()); - cerr << "STRING_ITEM: >" << valStr << '<' << endl; - break; - } - case REAL_RESULT: - { - cerr << "REAL_ITEM" << endl; - break; - } - case DECIMAL_RESULT: - { - cerr << "DECIMAL_ITEM" << endl; - break; - } - case TIME_RESULT: - { - String val, *str = NULL; - Item_temporal_literal* itp = (Item_temporal_literal*)item; - str = itp->val_str(&val); - cerr << "DATE ITEM: "; - - if (str) - cerr << ": (" << str->ptr() << ')' << endl; - else - cerr << ": " << endl; - - break; - } - default: - { - cerr << ": Unknown cmp_type" << endl; - break; - } - } - break; - } - case Item::FUNC_ITEM: - { - Item_func* ifp = (Item_func*)item; - Item_func_opt_neg* inp; - cerr << "FUNC_ITEM: "; - - switch (ifp->functype()) - { - case Item_func::UNKNOWN_FUNC: // 0 - cerr << ifp->func_name() << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::GT_FUNC: // 7 - cerr << '>' << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::EQ_FUNC: // 1 - cerr << '=' << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::GE_FUNC: cerr << ">=" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::LE_FUNC: cerr << "<=" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::LT_FUNC: cerr << '<' << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::NE_FUNC: cerr << "<>" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::NEG_FUNC: // 45 - cerr << "unary minus" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::IN_FUNC: // 16 - inp = (Item_func_opt_neg*)ifp; - - if (inp->negated) - cerr << "not "; - - cerr << "in" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::BETWEEN: - inp = (Item_func_opt_neg*)ifp; - - if (inp->negated) - cerr << "not "; - - cerr << "between" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::ISNULL_FUNC: // 10 - cerr << "is null" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::ISNOTNULL_FUNC: // 11 - cerr << "is not null" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::NOT_ALL_FUNC: cerr << "not_all" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::NOT_FUNC: cerr << "not_func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::TRIG_COND_FUNC: - cerr << "trig_cond_func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::ISNOTNULLTEST_FUNC: - cerr << "isnotnulltest_func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::MULT_EQUAL_FUNC: - { - cerr << "mult_equal_func:" << " (" << ifp->functype() << ")" << endl; - Item_equal* item_eq = (Item_equal*)ifp; - Item_equal_fields_iterator it(*item_eq); - Item* item; - - while ((item = it++)) - { - Field* equal_field = it.get_curr_field(); - cerr << equal_field->field_name.str << endl; - } - - break; - } - - case Item_func::EQUAL_FUNC: cerr << "equal func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::FT_FUNC: cerr << "ft func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::LIKE_FUNC: cerr << "like func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::COND_AND_FUNC: - cerr << "cond and func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::COND_OR_FUNC: cerr << "cond or func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::XOR_FUNC: cerr << "xor func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::INTERVAL_FUNC: - cerr << "interval func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_EQUALS_FUNC: - cerr << "sp equals func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_DISJOINT_FUNC: - cerr << "sp disjoint func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_INTERSECTS_FUNC: - cerr << "sp intersects func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_TOUCHES_FUNC: - cerr << "sp touches func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_CROSSES_FUNC: - cerr << "sp crosses func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_WITHIN_FUNC: - cerr << "sp within func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_CONTAINS_FUNC: - cerr << "sp contains func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_OVERLAPS_FUNC: - cerr << "sp overlaps func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_STARTPOINT: - cerr << "sp startpoint func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_ENDPOINT: - cerr << "sp endpoint func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_EXTERIORRING: - cerr << "sp exteriorring func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_POINTN: cerr << "sp pointn func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::SP_GEOMETRYN: - cerr << "sp geometryn func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_INTERIORRINGN: - cerr << "sp exteriorringn func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::SP_RELATE_FUNC: - cerr << "sp relate func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::NOW_FUNC: cerr << "now func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::SUSERVAR_FUNC: - cerr << "suservar func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::GUSERVAR_FUNC: - cerr << "guservar func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::COLLATE_FUNC: cerr << "collate func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::EXTRACT_FUNC: cerr << "extract func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::CHAR_TYPECAST_FUNC: - cerr << "char typecast func" << " (" << ifp->functype() << ")" << endl; - break; - - case Item_func::FUNC_SP: cerr << "func sp func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::UDF_FUNC: cerr << "udf func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::GSYSVAR_FUNC: cerr << "gsysvar func" << " (" << ifp->functype() << ")" << endl; break; - - case Item_func::DYNCOL_FUNC: cerr << "dyncol func" << " (" << ifp->functype() << ")" << endl; break; - - default: cerr << "type=" << ifp->functype() << endl; break; - } - - break; - } - - case Item::COND_ITEM: - { - Item_cond* icp = (Item_cond*)item; - cerr << "COND_ITEM: " << icp->func_name() << endl; - break; - } - - case Item::SUM_FUNC_ITEM: - { - Item_sum* isp = (Item_sum*)item; - char* item_name = const_cast(item->name.str); - - // MCOL-1052 This is an extended SELECT list item - if (!item_name && isp->get_arg_count() && isp->get_arg(0)->name.length) - { - item_name = const_cast(isp->get_arg(0)->name.str); - } - else if (!item_name && isp->get_arg_count() && isp->get_arg(0)->type() == Item::CONST_ITEM && - isp->get_arg(0)->cmp_type() == INT_RESULT) - { - item_name = (char*)"INT||*"; - } - else if (!item_name) - { - item_name = (char*)""; - } - - switch (isp->sum_func()) - { - case Item_sum::SUM_FUNC: cerr << "SUM_FUNC: " << item_name << endl; break; - - case Item_sum::SUM_DISTINCT_FUNC: cerr << "SUM_DISTINCT_FUNC: " << item_name << endl; break; - - case Item_sum::AVG_FUNC: cerr << "AVG_FUNC: " << item_name << endl; break; - - case Item_sum::COUNT_FUNC: cerr << "COUNT_FUNC: " << item_name << endl; break; - - case Item_sum::COUNT_DISTINCT_FUNC: cerr << "COUNT_DISTINCT_FUNC: " << item_name << endl; break; - - case Item_sum::MIN_FUNC: cerr << "MIN_FUNC: " << item_name << endl; break; - - case Item_sum::MAX_FUNC: cerr << "MAX_FUNC: " << item_name << endl; break; - - case Item_sum::UDF_SUM_FUNC: cerr << "UDAF_FUNC: " << item_name << endl; break; - - default: cerr << "SUM_FUNC_ITEM type=" << isp->sum_func() << endl; break; - } - - break; - } - - case Item::SUBSELECT_ITEM: - { - Item_subselect* sub = (Item_subselect*)item; - cerr << "SUBSELECT Item: "; - - switch (sub->substype()) - { - case Item_subselect::EXISTS_SUBS: cerr << "EXISTS"; break; - - case Item_subselect::IN_SUBS: cerr << "IN"; break; - - default: cerr << sub->substype(); break; - } - - cerr << endl; - JOIN* join = sub->get_select_lex()->join; - - if (join) - { - Item_cond* cond = static_cast(join->conds); - - if (cond) - cond->traverse_cond(debug_walk, arg, Item::POSTFIX); - } - - cerr << "Finish subselect item traversing" << endl; - break; - } - - case Item::REF_ITEM: - { - Item_ref* ref = (Item_ref*)item; - - if (ref->real_item()->type() == Item::CACHE_ITEM) - { - Item* field = ((Item_cache*)ref->real_item())->get_example(); - - if (field->type() == Item::FIELD_ITEM) - { - Item_field* ifp = (Item_field*)field; - // ifp->cached_table->select_lex->select_number gives the select level. - // could be used on alias. - // could also be used to tell correlated join (equal level). - cerr << "CACHED REF FIELD_ITEM: " << ifp->db_name.str << '.' << bestTableName(ifp) << '.' - << ifp->field_name.str << endl; - break; - } - else if (field->type() == Item::FUNC_ITEM) - { - Item_func* ifp = (Item_func*)field; - cerr << "CACHED REF FUNC_ITEM " << ifp->func_name() << endl; - } - else if (field->type() == Item::REF_ITEM) - { - Item_ref* ifr = (Item_ref*)field; - string refType; - string realType; - - switch (ifr->ref_type()) - { - case Item_ref::REF: refType = "REF"; break; - - case Item_ref::DIRECT_REF: refType = "DIRECT_REF"; break; - - case Item_ref::VIEW_REF: refType = "VIEW_REF"; break; - - case Item_ref::OUTER_REF: refType = "OUTER_REF"; break; - - case Item_ref::AGGREGATE_REF: refType = "AGGREGATE_REF"; break; - - default: refType = "UNKNOWN"; break; - } - - switch (ifr->real_type()) - { - case Item::FIELD_ITEM: - { - Item_field* ifp = (Item_field*)(*(ifr->ref)); - realType = "FIELD_ITEM "; - realType += ifp->db_name.str; - realType += '.'; - realType += bestTableName(ifp); - realType += '.'; - realType += ifp->field_name.str; - break; - } - - case Item::SUM_FUNC_ITEM: - { - Item_sum* isp = (Item_sum*)(*(ifr->ref)); - - if (isp->sum_func() == Item_sum::GROUP_CONCAT_FUNC) - realType = "GROUP_CONCAT_FUNC"; - else - realType = "SUM_FUNC_ITEM"; - - break; - } - - case Item::REF_ITEM: - // Need recursion here - realType = "REF_ITEM"; - break; - - case Item::FUNC_ITEM: - { - Item_func* ifp = (Item_func*)(*(ifr->ref)); - realType = "FUNC_ITEM "; - realType += ifp->func_name(); - break; - } - - default: - { - realType = "UNKNOWN"; - } - } - - cerr << "CACHED REF_ITEM: ref type " << refType.c_str() << " real type " << realType.c_str() - << endl; - break; - } - else - { - cerr << "REF_ITEM with CACHE_ITEM type unknown " << field->type() << endl; - } - } - else if (ref->real_item()->type() == Item::FIELD_ITEM) - { - Item_field* ifp = (Item_field*)ref->real_item(); - - // MCOL-1052 The field referenced presumable came from - // extended SELECT list. - if (!ifp->field_name.str) - { - cerr << "REF extra FIELD_ITEM: " << ifp->name.str << endl; - } - else - { - cerr << "REF FIELD_ITEM: " << ifp->db_name.str << '.' << bestTableName(ifp) << '.' - << ifp->field_name.str << endl; - } - - break; - } - else if (ref->real_item()->type() == Item::FUNC_ITEM) - { - Item_func* ifp = (Item_func*)ref->real_item(); - cerr << "REF FUNC_ITEM " << ifp->func_name() << endl; - } - else if (ref->real_item()->type() == Item::WINDOW_FUNC_ITEM) - { - Item_window_func* ifp = (Item_window_func*)ref->real_item(); - cerr << "REF WINDOW_FUNC_ITEM " << ifp->window_func()->func_name() << endl; - } - else - { - cerr << "UNKNOWN REF ITEM type " << ref->real_item()->type() << endl; - } - - break; - } - - case Item::ROW_ITEM: - { - Item_row* row = (Item_row*)item; - cerr << "ROW_ITEM: " << endl; - - for (uint32_t i = 0; i < row->cols(); i++) - debug_walk(row->element_index(i), 0); - - break; - } - - case Item::EXPR_CACHE_ITEM: - { - cerr << "Expr Cache Item" << endl; - ((Item_cache_wrapper*)item)->get_orig_item()->traverse_cond(debug_walk, arg, Item::POSTFIX); - break; - } - - case Item::CACHE_ITEM: - { - Item_cache* isp = (Item_cache*)item; - // MCOL-46 isp->val_str() can cause a call to execute a subquery. We're not set up - // to execute yet. -#if 0 - - switch (item->result_type()) - { - case STRING_RESULT: - cerr << "CACHE_STRING_ITEM" << endl; - break; - - case REAL_RESULT: - cerr << "CACHE_REAL_ITEM " << isp->val_real() << endl; - break; - - case INT_RESULT: - cerr << "CACHE_INT_ITEM " << isp->val_int() << endl; - break; - - case ROW_RESULT: - cerr << "CACHE_ROW_ITEM" << endl; - break; - - case DECIMAL_RESULT: - cerr << "CACHE_DECIMAL_ITEM " << isp->val_decimal() << endl; - break; - - default: - cerr << "CACHE_UNKNOWN_ITEM" << endl; - break; - } - -#endif - Item* field = isp->get_example(); - - if (field->type() == Item::FIELD_ITEM) - { - Item_field* ifp = (Item_field*)field; - // ifp->cached_table->select_lex->select_number gives the select level. - // could be used on alias. - // could also be used to tell correlated join (equal level). - cerr << "CACHED FIELD_ITEM: " << ifp->db_name.str << '.' << bestTableName(ifp) << '.' - << ifp->field_name.str << endl; - break; - } - else if (field->type() == Item::REF_ITEM) - { - Item_ref* ifr = (Item_ref*)field; - string refType; - string realType; - - switch (ifr->ref_type()) - { - case Item_ref::REF: refType = "REF"; break; - - case Item_ref::DIRECT_REF: refType = "DIRECT_REF"; break; - - case Item_ref::VIEW_REF: refType = "VIEW_REF"; break; - - case Item_ref::OUTER_REF: refType = "OUTER_REF"; break; - - case Item_ref::AGGREGATE_REF: refType = "AGGREGATE_REF"; break; - - default: refType = "UNKNOWN"; break; - } - - switch (ifr->real_type()) - { - case Item::FIELD_ITEM: - { - Item_field* ifp = (Item_field*)(*(ifr->ref)); - realType = "FIELD_ITEM "; - realType += ifp->db_name.str; - realType += '.'; - realType += bestTableName(ifp); - realType += '.'; - realType += ifp->field_name.str; - break; - } - - case Item::SUM_FUNC_ITEM: - { - Item_sum* isp = (Item_sum*)(*(ifr->ref)); - - if (isp->sum_func() == Item_sum::GROUP_CONCAT_FUNC) - realType = "GROUP_CONCAT_FUNC"; - else - realType = "SUM_FUNC_ITEM"; - - break; - } - - case Item::REF_ITEM: - // Need recursion here - realType = "REF_ITEM"; - break; - - case Item::FUNC_ITEM: - { - Item_func* ifp = (Item_func*)(*(ifr->ref)); - realType = "FUNC_ITEM "; - realType += ifp->func_name(); - break; - } - - default: - { - realType = "UNKNOWN"; - } - } - - cerr << "CACHE_ITEM ref type " << refType.c_str() << " real type " << realType.c_str() << endl; - break; - } - else if (field->type() == Item::FUNC_ITEM) - { - Item_func* ifp = (Item_func*)field; - cerr << "CACHE_ITEM FUNC_ITEM " << ifp->func_name() << endl; - break; - } - else - { - cerr << "CACHE_ITEM type unknown " << field->type() << endl; - } - - break; - } - - case Item::WINDOW_FUNC_ITEM: - { - Item_window_func* ifp = (Item_window_func*)item; - cerr << "Window Function Item " << ifp->window_func()->func_name() << endl; - break; - } - - case Item::NULL_ITEM: - { - cerr << "NULL item" << endl; - break; - } - - case Item::TYPE_HOLDER: - { - cerr << "TYPE_HOLDER item with cmp_type " << item->cmp_type() << endl; - break; - } - - default: - { - cerr << "UNKNOWN_ITEM type " << item->type() << endl; - break; - } - } -} -#endif - void buildNestedJoinLeafTables(List& join_list, std::set& leafTables) { @@ -3441,162 +2732,6 @@ ReturnedColumn* buildReturnedColumnNull(gp_walk_info& gwi) return rc; } -class ValStrStdString : public string -{ - bool mIsNull; - - public: - ValStrStdString(Item* item) - { - String val, *str = item->val_str(&val); - mIsNull = (str == nullptr); - DBUG_ASSERT(mIsNull == item->null_value); - if (!mIsNull) - assign(str->ptr(), str->length()); - } - bool isNull() const - { - return mIsNull; - } -}; - -/* - Create a ConstantColumn according to cmp_type(). - But do not set the time zone yet. - - Handles NOT NULL values. - - Three ways of value extraction are used depending on the data type: - 1. Using a native val_xxx(). - 2. Using val_str() with further convertion to the native representation. - 3. Using both val_str() and a native val_xxx(). - - We should eventually get rid of N2 and N3 and use N1 for all data types: - - N2 contains a redundant code for str->native conversion. - It should be replaced to an existing code (a Type_handler method call?). - - N3 performs double evalation of the value, which may cause - various negative effects (double side effects or double warnings). -*/ -static ConstantColumn* newConstantColumnNotNullUsingValNativeNoTz(Item* item, gp_walk_info& gwi) -{ - DBUG_ASSERT(item->const_item()); - - switch (item->cmp_type()) - { - case INT_RESULT: - { - if (item->unsigned_flag) - return new ConstantColumnUInt((uint64_t)item->val_uint(), (int8_t)item->decimal_scale(), - (uint8_t)item->decimal_precision()); - ValStrStdString str(item); - DBUG_ASSERT(!str.isNull()); - return new ConstantColumnSInt(colType_MysqlToIDB(item), str, (int64_t)item->val_int()); - } - case STRING_RESULT: - { - // Special handling for 0xHHHH literals - if (item->type_handler() == &type_handler_hex_hybrid) - return new ConstantColumn((int64_t)item->val_int(), ConstantColumn::NUM); - ValStrStdString str(item); - DBUG_ASSERT(!str.isNull()); - return new ConstantColumnString(str); - } - case REAL_RESULT: - { - ValStrStdString str(item); - DBUG_ASSERT(!str.isNull()); - return new ConstantColumnReal(colType_MysqlToIDB(item), str, item->val_real()); - } - case DECIMAL_RESULT: - { - ValStrStdString str(item); - DBUG_ASSERT(!str.isNull()); - return buildDecimalColumn(item, str, gwi); - } - case TIME_RESULT: - { - ValStrStdString str(item); - DBUG_ASSERT(!str.isNull()); - return new ConstantColumnTemporal(colType_MysqlToIDB(item), str); - } - default: - { - gwi.fatalParseError = true; - gwi.parseErrorText = "Unknown item type"; - break; - } - } - - return nullptr; -} - -/* - Create a ConstantColumn according to cmp_type(). - But do not set the time zone yet. - - Handles NULL and NOT NULL values. - - Uses a simplified logic regarding to data types: - always extracts the value through val_str(). - - Should probably be joined with the previous function, to have - a single function which can at the same time: - a. handle both NULL and NOT NULL values - b. extract values using a native val_xxx() method, - to avoid possible negative effects mentioned in the comments - to newConstantColumnNotNullUsingValNativeNoTz(). -*/ -static ConstantColumn* newConstantColumnMaybeNullFromValStrNoTz(const Item* item, - const ValStrStdString& valStr, - gp_walk_info& gwi) -{ - if (valStr.isNull()) - return new ConstantColumnNull(); - - switch (item->result_type()) - { - case STRING_RESULT: return new ConstantColumnString(valStr); - case DECIMAL_RESULT: return buildDecimalColumn(item, valStr, gwi); - case TIME_RESULT: - case INT_RESULT: - case REAL_RESULT: - case ROW_RESULT: return new ConstantColumnNum(colType_MysqlToIDB(item), valStr); - } - return nullptr; -} - -// Create a ConstantColumn from a previously evaluated val_str() result, -// Supports both NULL and NOT NULL values. -// Sets the time zone according to gwi. - -static ConstantColumn* buildConstantColumnMaybeNullFromValStr(const Item* item, const ValStrStdString& valStr, - gp_walk_info& gwi) -{ - ConstantColumn* rc = newConstantColumnMaybeNullFromValStrNoTz(item, valStr, gwi); - if (rc) - rc->timeZone(gwi.timeZone); - return rc; -} - -// Create a ConstantColumn by calling val_str(). -// Supports both NULL and NOT NULL values. -// Sets the time zone according to gwi. - -static ConstantColumn* buildConstantColumnMaybeNullUsingValStr(Item* item, gp_walk_info& gwi) -{ - return buildConstantColumnMaybeNullFromValStr(item, ValStrStdString(item), gwi); -} - -// Create a ConstantColumn for a NOT NULL expression. -// Sets the time zone according to gwi. -static ConstantColumn* buildConstantColumnNotNullUsingValNative(Item* item, gp_walk_info& gwi) -{ - ConstantColumn* rc = newConstantColumnNotNullUsingValNativeNoTz(item, gwi); - if (rc) - rc->timeZone(gwi.timeZone); - return rc; -} - ReturnedColumn* buildReturnedColumnBody(Item* item, gp_walk_info& gwi, bool& nonSupport, bool /*isRefItem*/) { ReturnedColumn* rc = NULL; @@ -5092,21 +4227,6 @@ class ConstArgParam } }; -static bool isSupportedAggregateWithOneConstArg(const Item_sum* item, Item** orig_args) -{ - if (item->argument_count() != 1 || !orig_args[0]->const_item()) - return false; - switch (orig_args[0]->cmp_type()) - { - case INT_RESULT: - case STRING_RESULT: - case REAL_RESULT: - case DECIMAL_RESULT: return true; - default: break; - } - return false; -} - static void processAggregateColumnConstArg(gp_walk_info& gwi, SRCP& parm, AggregateColumn* ac, Item* sfitemp, ConstArgParam& constParam) { @@ -5961,921 +5081,6 @@ void castTypeArgs(gp_walk_info* gwip, Item_func* ifp, FunctionParm& functionParm functionParms.push_back(sptp); } -bool isSecondArgumentConstItem(Item_func* ifp) -{ - return (ifp->argument_count() == 2 && ifp->arguments()[1]->type() == Item::CONST_ITEM); -} - -// SELECT ... WHERE NOT IN (SELECT ); -bool isNotFuncAndConstScalarSubSelect(Item_func* ifp, const std::string& funcName) -{ - return (ifp->with_subquery() && funcName == "not" && ifp->argument_count() == 1 && - ifp->arguments()[0]->type() == Item::FUNC_ITEM && - std::string(((Item_func*)ifp->arguments()[0])->func_name()) == "=" && - isSecondArgumentConstItem((Item_func*)ifp->arguments()[0])); -} - -void gp_walk(const Item* item, void* arg) -{ - gp_walk_info* gwip = static_cast(arg); - idbassert(gwip); - - // Bailout... - if (gwip->fatalParseError) - return; - - RecursionCounter r(gwip); // Increments and auto-decrements upon exit. - - Item::Type itype = item->type(); - - // Allow to process XOR(which is Item_func) like other logical operators (which are Item_cond) - if (itype == Item::FUNC_ITEM && ((Item_func*)item)->functype() == Item_func::XOR_FUNC) - itype = Item::COND_ITEM; - - switch (itype) - { - case Item::CACHE_ITEM: - { - // The item or condition is cached as per MariaDB server view but - // for InfiniDB it need to be parsed and executed. - // MCOL-1188 and MCOL-1029 - Item* orig_item = ((Item_cache*)item)->get_example(); - orig_item->traverse_cond(gp_walk, gwip, Item::POSTFIX); - break; - } - case Item::FIELD_ITEM: - { - Item_field* ifp = (Item_field*)item; - - if (ifp) - { - // XXX: this looks awfuly wrong. - SimpleColumn* scp = buildSimpleColumn(ifp, *gwip); - - if (!scp) - break; - - string aliasTableName(scp->tableAlias()); - scp->tableAlias(aliasTableName); - gwip->rcWorkStack.push(scp->clone()); - boost::shared_ptr scsp(scp); - gwip->scsp = scsp; - - gwip->funcName.clear(); - gwip->columnMap.insert( - CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name.str), scsp)); - - //@bug4636 take where clause column as dummy projection column, but only on local column. - // varbinary aggregate is not supported yet, so rule it out - if (!((scp->joinInfo() & JOIN_CORRELATED) || - scp->colType().colDataType == CalpontSystemCatalog::VARBINARY)) - { - TABLE_LIST* tmp = (ifp->cached_table ? ifp->cached_table : 0); - gwip->tableMap[make_aliastable(scp->schemaName(), scp->tableName(), scp->tableAlias(), - scp->isColumnStore())] = make_pair(1, tmp); - } - } - - break; - } - - case Item::CONST_ITEM: - { - switch (item->cmp_type()) - { - case INT_RESULT: - { - Item* non_const_item = const_cast(item); - gwip->rcWorkStack.push(buildReturnedColumn(non_const_item, *gwip, gwip->fatalParseError)); - break; - } - - case STRING_RESULT: - { - // Special handling for 0xHHHH literals - if (item->type_handler() == &type_handler_hex_hybrid) - { - Item_hex_hybrid* hip = static_cast(const_cast(item)); - gwip->rcWorkStack.push(new ConstantColumn((int64_t)hip->val_int(), ConstantColumn::NUM)); - ConstantColumn* cc = dynamic_cast(gwip->rcWorkStack.top()); - cc->timeZone(gwip->timeZone); - break; - } - - if (item->result_type() == STRING_RESULT) - { - // dangerous cast here - Item* isp = const_cast(item); - String val, *str = isp->val_str(&val); - if (str) - { - string cval; - - if (str->ptr()) - { - cval.assign(str->ptr(), str->length()); - } - - gwip->rcWorkStack.push(new ConstantColumn(cval)); - (dynamic_cast(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone); - break; - } - else - { - gwip->rcWorkStack.push(new ConstantColumn("", ConstantColumn::NULLDATA)); - (dynamic_cast(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone); - break; - } - - gwip->rcWorkStack.push(buildReturnedColumn(isp, *gwip, gwip->fatalParseError)); - } - break; - } - - case REAL_RESULT: - case DECIMAL_RESULT: - case TIME_RESULT: - { - Item* nonConstItem = const_cast(item); - gwip->rcWorkStack.push(buildReturnedColumn(nonConstItem, *gwip, gwip->fatalParseError)); - break; - } - - default: - { - if (gwip->condPush) - { - // push noop for unhandled item - SimpleColumn* rc = new SimpleColumn("noop"); - rc->timeZone(gwip->timeZone); - gwip->rcWorkStack.push(rc); - break; - } - - ostringstream oss; - oss << "Unhandled Item type(): " << item->type(); - gwip->parseErrorText = oss.str(); - gwip->fatalParseError = true; - break; - } - } - break; - } - case Item::NULL_ITEM: - { - if (gwip->condPush) - { - // push noop for unhandled item - SimpleColumn* rc = new SimpleColumn("noop"); - rc->timeZone(gwip->timeZone); - gwip->rcWorkStack.push(rc); - break; - } - - gwip->rcWorkStack.push(new ConstantColumn("", ConstantColumn::NULLDATA)); - (dynamic_cast(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone); - break; - } - - case Item::FUNC_ITEM: - { - Item* ncitem = const_cast(item); - Item_func* ifp = static_cast(ncitem); - - string funcName = ifp->func_name(); - - if (!gwip->condPush) - { - if (!ifp->fixed()) - { - ifp->fix_fields(gwip->thd, &ncitem); - } - - // Special handling for queries of the form: - // SELECT ... WHERE col1 NOT IN (SELECT ); - if (isNotFuncAndConstScalarSubSelect(ifp, funcName)) - { - idbassert(!gwip->ptWorkStack.empty()); - ParseTree* pt = gwip->ptWorkStack.top(); - SimpleFilter* sf = dynamic_cast(pt->data()); - - if (sf) - { - boost::shared_ptr sop(new PredicateOperator("<>")); - sf->op(sop); - return; - } - } - - // Do not call buildSubselectFunc() if the subquery is a const scalar - // subselect of the form: - // (SELECT ) - // As an example: SELECT col1 FROM t1 WHERE col2 = (SELECT 2); - if ((ifp->with_subquery() && !isSecondArgumentConstItem(ifp)) || funcName == "") - { - buildSubselectFunc(ifp, gwip); - return; - } - - if (ifp->argument_count() > 0 && ifp->arguments()) - { - for (uint32_t i = 0; i < ifp->argument_count(); i++) - { - if (ifp->arguments()[i]->type() == Item::SUBSELECT_ITEM) - { - // This is probably NOT IN subquery with derived table in it. - // for some reason, MySQL has not fully optimized the plan at this point. - // noop here, and eventually MySQL will continue its optimization and get - // to rnd_init again. - if (ifp->functype() == Item_func::NOT_FUNC) - return; - - buildSubselectFunc(ifp, gwip); - return; - } - } - } - - if (ifp->functype() == Item_func::TRIG_COND_FUNC && gwip->subQuery) - { - gwip->subQuery->handleFunc(gwip, ifp); - break; - } - - // having clause null function added by MySQL - if (ifp->functype() == Item_func::ISNOTNULLTEST_FUNC) - { - // @bug 4215. remove the argument in rcWorkStack. - if (!gwip->rcWorkStack.empty()) - { - delete gwip->rcWorkStack.top(); - gwip->rcWorkStack.pop(); - } - - break; - } - } - - // try to evaluate const F&E - vector tmpVec; - uint16_t parseInfo = 0; - parse_item(ifp, tmpVec, gwip->fatalParseError, parseInfo, gwip); - - // table mode takes only one table filter - if (gwip->condPush) - { - set tableSet; - - for (uint32_t i = 0; i < tmpVec.size(); i++) - { - if (tmpVec[i]->table_name.str) - tableSet.insert(tmpVec[i]->table_name.str); - } - - if (tableSet.size() > 1) - break; - } - - if (!gwip->fatalParseError && !(parseInfo & AGG_BIT) && !(parseInfo & SUB_BIT) && !nonConstFunc(ifp) && - !(parseInfo & AF_BIT) && tmpVec.size() == 0 && ifp->functype() != Item_func::MULT_EQUAL_FUNC) - { - ValStrStdString valStr(ifp); - - ConstantColumn* cc = buildConstantColumnMaybeNullFromValStr(ifp, valStr, *gwip); - - for (uint32_t i = 0; i < ifp->argument_count() && !gwip->rcWorkStack.empty(); i++) - { - delete gwip->rcWorkStack.top(); - gwip->rcWorkStack.pop(); - } - - // bug 3137. If filter constant like 1=0, put it to ptWorkStack - // MariaDB bug 750. Breaks if compare is an argument to a function. - // if ((int32_t)gwip->rcWorkStack.size() <= - //(gwip->rcBookMarkStack.empty() - //? - // 0 - //: gwip->rcBookMarkStack.top()) - // && isPredicateFunction(ifp, gwip)) - if (isPredicateFunction(ifp, gwip)) - gwip->ptWorkStack.push(new ParseTree(cc)); - else - gwip->rcWorkStack.push(cc); - - if (!valStr.isNull()) - IDEBUG(cerr << "Const F&E " << item->full_name() << " evaluate: " << valStr << endl); - - break; - } - - ReturnedColumn* rc = NULL; - - // @bug4488. Process function for table mode also, not just vtable mode. - rc = buildFunctionColumn(ifp, *gwip, gwip->fatalParseError); - - if (gwip->fatalParseError) - { - if (gwip->clauseType == SELECT) - return; - - // @bug 2585 - if (gwip->parseErrorText.empty()) - { - Message::Args args; - args.add(funcName); - gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORTED_FUNCTION, args); - } - - return; - } - - // predicate operators fall in the old path - if (rc) - { - // @bug 2383. For some reason func_name() for "in" gives " IN " always - if (funcName == "between" || funcName == "in" || funcName == " IN ") - gwip->ptWorkStack.push(new ParseTree(rc)); - else - gwip->rcWorkStack.push(rc); - } - else - { - // push to pt or rc stack is handled inside the function - buildPredicateItem(ifp, gwip); - } - - break; - } - - case Item::SUM_FUNC_ITEM: - { - Item_sum* isp = (Item_sum*)item; - ReturnedColumn* rc = buildAggregateColumn(isp, *gwip); - - if (rc) - gwip->rcWorkStack.push(rc); - - break; - } - - case Item::COND_ITEM: - { - // All logical functions are handled here, most of them are Item_cond, - // but XOR (it is Item_func_boolean2) - Item_func* func = (Item_func*)item; - - enum Item_func::Functype ftype = func->functype(); - bool isOr = (ftype == Item_func::COND_OR_FUNC); - bool isXor = (ftype == Item_func::XOR_FUNC); - - List* argumentList; - List xorArgumentList; - - if (isXor) - { - for (unsigned i = 0; i < func->argument_count(); i++) - { - xorArgumentList.push_back(func->arguments()[i]); - } - - argumentList = &xorArgumentList; - } - else - { - argumentList = ((Item_cond*)item)->argument_list(); - } - - // @bug2932. if ptWorkStack contains less items than the condition's arguments, - // the missing one should be in the rcWorkStack, unless the it's subselect. - // @todo need to figure out a way to combine these two stacks while walking. - // if (gwip->ptWorkStack.size() < icp->argument_list()->elements) - { - List_iterator_fast li(*argumentList); - - while (Item* it = li++) - { - //@bug3495, @bug5865 error out non-supported OR with correlated subquery - if (isOr) - { - vector fieldVec; - uint16_t parseInfo = 0; - parse_item(it, fieldVec, gwip->fatalParseError, parseInfo, gwip); - - if (parseInfo & CORRELATED) - { - gwip->fatalParseError = true; - gwip->parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_CORRELATED_SUB_OR); - return; - } - } - - if ((it->type() == Item::FIELD_ITEM || - (it->type() == Item::CONST_ITEM && - (it->cmp_type() == INT_RESULT || it->cmp_type() == DECIMAL_RESULT || - it->cmp_type() == STRING_RESULT || it->cmp_type() == REAL_RESULT)) || - it->type() == Item::NULL_ITEM || - (it->type() == Item::FUNC_ITEM && !isPredicateFunction(it, gwip))) && - !gwip->rcWorkStack.empty()) - { - gwip->ptWorkStack.push(new ParseTree(gwip->rcWorkStack.top())); - gwip->rcWorkStack.pop(); - } - } - } - - // @bug1603. MySQL's filter tree is a multi-tree grouped by operator. So more than - // two filters saved on the stack so far might belong to this operator. - uint32_t leftInStack = gwip->ptWorkStack.size() - argumentList->elements + 1; - - while (true) - { - if (gwip->ptWorkStack.size() < 2) - break; - - ParseTree* lhs = gwip->ptWorkStack.top(); - gwip->ptWorkStack.pop(); - SimpleFilter* lsf = dynamic_cast(lhs->data()); - - if (lsf && lsf->op()->data() == "noop") - { - if (isOr) - { - gwip->parseErrorText = "Unhandled item in WHERE or HAVING clause"; - gwip->fatalParseError = true; - break; - } - else - continue; - } - - ParseTree* rhs = gwip->ptWorkStack.top(); - gwip->ptWorkStack.pop(); - SimpleFilter* rsf = dynamic_cast(rhs->data()); - - if (rsf && rsf->op()->data() == "noop") - { - if (isOr) - { - gwip->parseErrorText = "Unhandled item in WHERE or HAVING clause"; - gwip->fatalParseError = true; - break; - } - else - { - delete rhs; - gwip->ptWorkStack.push(lhs); - continue; - } - } - - Operator* op = new LogicOperator(func->func_name()); - ParseTree* ptp = new ParseTree(op); - ptp->left(lhs); - ptp->right(rhs); - gwip->ptWorkStack.push(ptp); - - if (gwip->ptWorkStack.size() == leftInStack) - break; - } - - // special handling for subquery with aggregate. MySQL adds isnull function to the selected - // column. InfiniDB will remove it and set nullmatch flag if it's NOT_IN sub. - // @todo need more checking here to make sure it's not a user input OR operator - if (isOr && gwip->subQuery) - gwip->subQuery->handleFunc(gwip, func); - - break; - } - - case Item::REF_ITEM: - { - Item* col = *(((Item_ref*)item)->ref); - ReturnedColumn* rc = NULL; - // ref item is not pre-walked. force clause type to SELECT - ClauseType clauseType = gwip->clauseType; - gwip->clauseType = SELECT; - - if (col->type() != Item::COND_ITEM) - { - rc = buildReturnedColumn(col, *gwip, gwip->fatalParseError, true); - - if (col->type() == Item::FIELD_ITEM) - gwip->fatalParseError = false; - } - - SimpleColumn* sc = clauseType == HAVING ? nullptr : dynamic_cast(rc); - - if (sc) - { - boost::shared_ptr scsp(sc->clone()); - gwip->scsp = scsp; - - if (col->type() == Item::FIELD_ITEM) - { - const Item_ident* ident_field = dynamic_cast(item); - if (ident_field) - { - const auto& field_name = string(ident_field->field_name.str); - auto colMap = CalpontSelectExecutionPlan::ColumnMap::value_type(field_name, scsp); - gwip->columnMap.insert(colMap); - } - } - } - - bool cando = true; - gwip->clauseType = clauseType; - - if (rc) - { - if (((Item_ref*)item)->depended_from) - { - rc->joinInfo(rc->joinInfo() | JOIN_CORRELATED); - - if (gwip->subQuery) - gwip->subQuery->correlated(true); - - SimpleColumn* scp = dynamic_cast(rc); - - if (scp) - gwip->correlatedTbNameVec.push_back( - make_aliastable(scp->schemaName(), scp->tableName(), scp->tableAlias())); - - if (gwip->subSelectType == CalpontSelectExecutionPlan::SINGLEROW_SUBS) - rc->joinInfo(rc->joinInfo() | JOIN_SCALAR | JOIN_SEMI); - - if (gwip->subSelectType == CalpontSelectExecutionPlan::SELECT_SUBS) - rc->joinInfo(rc->joinInfo() | JOIN_SCALAR | JOIN_OUTER_SELECT); - } - - gwip->rcWorkStack.push(rc); - } - else if (col->type() == Item::FUNC_ITEM) - { - // sometimes mysql treat having filter items inconsistently. In such cases, - // which are always predicate operator, the function (gp_key>3) comes in as - // one item. - Item_func* ifp = (Item_func*)col; - - for (uint32_t i = 0; i < ifp->argument_count(); i++) - { - ReturnedColumn* operand = NULL; - - if (ifp->arguments()[i]->type() == Item::REF_ITEM) - { - Item* op = *(((Item_ref*)ifp->arguments()[i])->ref); - operand = buildReturnedColumn(op, *gwip, gwip->fatalParseError); - } - else - operand = buildReturnedColumn(ifp->arguments()[i], *gwip, gwip->fatalParseError); - - if (operand) - { - gwip->rcWorkStack.push(operand); - if (i == 0 && gwip->scsp == NULL) // first item is the WHEN LHS - { - SimpleColumn* sc = dynamic_cast(operand); - if (sc) - { - gwip->scsp.reset(sc->clone()); // We need to clone else sc gets double deleted. This code is - // rarely executed so the cost is acceptable. - } - } - } - else - { - cando = false; - break; - } - } - - if (cando) - buildPredicateItem(ifp, gwip); - } - else if (col->type() == Item::COND_ITEM) - { - gwip->ptWorkStack.push(buildParseTree(col, *gwip, gwip->fatalParseError)); - } - else if (col->type() == Item::FIELD_ITEM && gwip->clauseType == HAVING) - { - // ReturnedColumn* rc = buildAggFrmTempField(const_cast(item), *gwip); - ReturnedColumn* rc = buildReturnedColumn(const_cast(item), *gwip, gwip->fatalParseError); - if (rc) - gwip->rcWorkStack.push(rc); - - break; - } - else - { - cando = false; - } - - SimpleColumn* thisSC = dynamic_cast(rc); - if (thisSC) - { - gwip->scsp.reset(thisSC->clone()); - } - if (!rc && !cando) - { - ostringstream oss; - oss << "Unhandled Item type(): " << item->type(); - gwip->parseErrorText = oss.str(); - gwip->fatalParseError = true; - } - - break; - } - - case Item::SUBSELECT_ITEM: - { - if (gwip->condPush) // table mode - break; - - Item_subselect* sub = (Item_subselect*)item; - - if (sub->substype() == Item_subselect::EXISTS_SUBS) - { - SubQuery* orig = gwip->subQuery; - ExistsSub* existsSub = new ExistsSub(*gwip, sub); - gwip->hasSubSelect = true; - gwip->subQuery = existsSub; - gwip->ptWorkStack.push(existsSub->transform()); - // recover original - gwip->subQuery = orig; - gwip->lastSub = existsSub; - } - else if (sub->substype() == Item_subselect::IN_SUBS) - { - if (!((Item_in_subselect*)sub)->optimizer && gwip->thd->derived_tables_processing) - { - ostringstream oss; - oss << "Invalid In_optimizer: " << item->type(); - gwip->parseErrorText = oss.str(); - gwip->fatalParseError = true; - break; - } - } - - // store a dummy subselect object. the transform is handled in item_func. - SubSelect* subselect = new SubSelect(); - gwip->rcWorkStack.push(subselect); - break; - } - - case Item::ROW_ITEM: - { - Item_row* row = (Item_row*)item; - RowColumn* rowCol = new RowColumn(); - vector cols; - // temp change clause type because the elements of row column are not walked yet - gwip->clauseType = SELECT; - for (uint32_t i = 0; i < row->cols(); i++) - cols.push_back(SRCP(buildReturnedColumn(row->element_index(i), *gwip, gwip->fatalParseError))); - - gwip->clauseType = WHERE; - rowCol->columnVec(cols); - gwip->rcWorkStack.push(rowCol); - break; - } - - case Item::EXPR_CACHE_ITEM: - { - ((Item_cache_wrapper*)item)->get_orig_item()->traverse_cond(gp_walk, arg, Item::POSTFIX); - break; - } - - case Item::WINDOW_FUNC_ITEM: - { - gwip->hasWindowFunc = true; - Item_window_func* ifa = (Item_window_func*)item; - ReturnedColumn* af = buildWindowFunctionColumn(ifa, *gwip, gwip->fatalParseError); - - if (af) - gwip->rcWorkStack.push(af); - - break; - } - - case Item::COPY_STR_ITEM: printf("********** received COPY_STR_ITEM *********\n"); break; - - case Item::FIELD_AVG_ITEM: printf("********** received FIELD_AVG_ITEM *********\n"); break; - - case Item::DEFAULT_VALUE_ITEM: printf("********** received DEFAULT_VALUE_ITEM *********\n"); break; - - case Item::PROC_ITEM: printf("********** received PROC_ITEM *********\n"); break; - - case Item::FIELD_STD_ITEM: printf("********** received FIELD_STD_ITEM *********\n"); break; - - case Item::FIELD_VARIANCE_ITEM: printf("********** received FIELD_VARIANCE_ITEM *********\n"); break; - - case Item::INSERT_VALUE_ITEM: printf("********** received INSERT_VALUE_ITEM *********\n"); break; - - case Item::PARAM_ITEM: printf("********** received PARAM_ITEM *********\n"); break; - - case Item::TRIGGER_FIELD_ITEM: printf("********** received TRIGGER_FIELD_ITEM *********\n"); break; - - case Item::TYPE_HOLDER: std::cerr << "********** received TYPE_HOLDER *********" << std::endl; break; - default: - { - if (gwip->condPush) - { - // push noop for unhandled item - SimpleColumn* rc = new SimpleColumn("noop"); - rc->timeZone(gwip->timeZone); - gwip->rcWorkStack.push(rc); - break; - } - - ostringstream oss; - oss << "Unhandled Item type (2): " << item->type(); - gwip->parseErrorText = oss.str(); - gwip->fatalParseError = true; - break; - } - } - - return; -} - -/** @info this function recursivly walks an item's arguments and push all - * the involved item_fields to the passed in vector. It's used in parsing - * functions or arithmetic expressions for vtable post process. - */ -void parse_item(Item* item, vector& field_vec, bool& hasNonSupportItem, uint16_t& parseInfo, - gp_walk_info* gwi) -{ - Item::Type itype = item->type(); - - switch (itype) - { - case Item::FIELD_ITEM: - { - Item_field* ifp = static_cast(item); - field_vec.push_back(ifp); - return; - } - - case Item::SUM_FUNC_ITEM: - { - // hasAggColumn = true; - parseInfo |= AGG_BIT; - Item_sum* isp = static_cast(item); - Item** sfitempp = isp->arguments(); - - for (uint32_t i = 0; i < isp->argument_count(); i++) - parse_item(sfitempp[i], field_vec, hasNonSupportItem, parseInfo, gwi); - - break; - } - - case Item::FUNC_ITEM: - { - Item_func* isp = static_cast(item); - - if (string(isp->func_name()) == "") - { - parseInfo |= SUB_BIT; - parseInfo |= CORRELATED; - break; - } - - for (uint32_t i = 0; i < isp->argument_count(); i++) - parse_item(isp->arguments()[i], field_vec, hasNonSupportItem, parseInfo, gwi); - - break; - } - - case Item::COND_ITEM: - { - Item_cond* icp = static_cast(item); - List_iterator_fast it(*(icp->argument_list())); - Item* cond_item; - - while ((cond_item = it++)) - parse_item(cond_item, field_vec, hasNonSupportItem, parseInfo, gwi); - - break; - } - - case Item::REF_ITEM: - { - Item_ref* ref = (Item_ref*)item; - if (ref->ref_type() == Item_ref::DIRECT_REF) - { - parse_item(ref->real_item(), field_vec, hasNonSupportItem, parseInfo, gwi); - break; - } - while (true) - { - ref = (Item_ref*)item; - if ((*(ref->ref))->type() == Item::SUM_FUNC_ITEM) - { - parseInfo |= AGG_BIT; - Item_sum* isp = static_cast(*(ref->ref)); - Item** sfitempp = isp->arguments(); - - // special handling for count(*). This should not be treated as constant. - if (isSupportedAggregateWithOneConstArg(isp, sfitempp)) - { - field_vec.push_back(nullptr); // dummy - } - - for (uint32_t i = 0; i < isp->argument_count(); i++) - parse_item(sfitempp[i], field_vec, hasNonSupportItem, parseInfo, gwi); - - break; - } - else if ((*(ref->ref))->type() == Item::FIELD_ITEM) - { - // MCOL-1510. This could be a non-supported function - // argument in form of a temp_table_field, so check - // and set hasNonSupportItem if it is so. - // ReturnedColumn* rc = NULL; - // if (gwi) - // rc = buildAggFrmTempField(ref, *gwi); - - // if (!rc) - //{ - Item_field* ifp = static_cast(*(ref->ref)); - field_vec.push_back(ifp); - //} - break; - } - else if ((*(ref->ref))->type() == Item::FUNC_ITEM) - { - Item_func* isp = static_cast(*(ref->ref)); - Item** sfitempp = isp->arguments(); - - for (uint32_t i = 0; i < isp->argument_count(); i++) - parse_item(sfitempp[i], field_vec, hasNonSupportItem, parseInfo, gwi); - - break; - } - else if ((*(ref->ref))->type() == Item::CACHE_ITEM) - { - Item_cache* isp = static_cast(*(ref->ref)); - parse_item(isp->get_example(), field_vec, hasNonSupportItem, parseInfo, gwi); - break; - } - else if ((*(ref->ref))->type() == Item::REF_ITEM) - { - item = (*(ref->ref)); - continue; - } - else if ((*(ref->ref))->type() == Item::WINDOW_FUNC_ITEM) - { - parseInfo |= AF_BIT; - break; - } - else - { - cerr << "UNKNOWN REF Item" << endl; - break; - } - } - - break; - } - - case Item::SUBSELECT_ITEM: - { - parseInfo |= SUB_BIT; - Item_subselect* sub = (Item_subselect*)item; - - if (sub->is_correlated) - parseInfo |= CORRELATED; - - break; - } - - case Item::ROW_ITEM: - { - Item_row* row = (Item_row*)item; - - for (uint32_t i = 0; i < row->cols(); i++) - parse_item(row->element_index(i), field_vec, hasNonSupportItem, parseInfo, gwi); - - break; - } - - case Item::EXPR_CACHE_ITEM: - { - // item is a Item_cache_wrapper. Shouldn't get here. - // DRRTUY TODO Why - IDEBUG(std::cerr << "EXPR_CACHE_ITEM in parse_item\n" << std::endl); - gwi->fatalParseError = true; - // DRRTUY The questionable error text. I've seen - // ERR_CORRELATED_SUB_OR - string parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SUB_QUERY_TYPE); - setError(gwi->thd, ER_CHECK_NOT_IMPLEMENTED, parseErrorText); - break; - } - - case Item::WINDOW_FUNC_ITEM: parseInfo |= AF_BIT; break; - - default: break; - } -} - /*@brief set some runtime params to run the query */ /*********************************************************** * DESCRIPTION: diff --git a/dbcon/mysql/ha_mcs_execplan_helpers.cpp b/dbcon/mysql/ha_mcs_execplan_helpers.cpp new file mode 100644 index 000000000..66ffa35be --- /dev/null +++ b/dbcon/mysql/ha_mcs_execplan_helpers.cpp @@ -0,0 +1,189 @@ +/* Copyright (C) 2025 MariaDB Corporation + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "ha_mcs_execplan_helpers.h" +#include "constantcolumn.h" + +namespace cal_impl_if +{ +bool nonConstFunc(Item_func* ifp) +{ + if (strcasecmp(ifp->func_name(), "rand") == 0 || strcasecmp(ifp->func_name(), "sysdate") == 0 || + strcasecmp(ifp->func_name(), "idblocalpm") == 0) + return true; + + for (uint32_t i = 0; i < ifp->argument_count(); i++) + { + if (ifp->arguments()[i]->type() == Item::FUNC_ITEM && nonConstFunc(((Item_func*)ifp->arguments()[i]))) + return true; + } + + return false; +} + +/* + Create a ConstantColumn according to cmp_type(). + But do not set the time zone yet. + + Handles NULL and NOT NULL values. + + Uses a simplified logic regarding to data types: + always extracts the value through val_str(). + + Should probably be joined with the previous function, to have + a single function which can at the same time: + a. handle both NULL and NOT NULL values + b. extract values using a native val_xxx() method, + to avoid possible negative effects mentioned in the comments + to newConstantColumnNotNullUsingValNativeNoTz(). +*/ +execplan::ConstantColumn* newConstantColumnMaybeNullFromValStrNoTz(const Item* item, + const ValStrStdString& valStr, + gp_walk_info& gwi) +{ + if (valStr.isNull()) + return new execplan::ConstantColumnNull(); + + switch (item->result_type()) + { + case STRING_RESULT: return new execplan::ConstantColumnString(valStr); + case DECIMAL_RESULT: return buildDecimalColumn(item, valStr, gwi); + case TIME_RESULT: + case INT_RESULT: + case REAL_RESULT: + case ROW_RESULT: return new execplan::ConstantColumnNum(colType_MysqlToIDB(item), valStr); + } + return nullptr; +} + +// Create a ConstantColumn from a previously evaluated val_str() result, +// Supports both NULL and NOT NULL values. +// Sets the time zone according to gwi. +execplan::ConstantColumn* buildConstantColumnMaybeNullFromValStr(const Item* item, const ValStrStdString& valStr, + gp_walk_info& gwi) +{ + execplan::ConstantColumn* rc = newConstantColumnMaybeNullFromValStrNoTz(item, valStr, gwi); + if (rc) + rc->timeZone(gwi.timeZone); + return rc; +} + +// Create a ConstantColumn by calling val_str(). +// Supports both NULL and NOT NULL values. +// Sets the time zone according to gwi. + +execplan::ConstantColumn* buildConstantColumnMaybeNullUsingValStr(Item* item, gp_walk_info& gwi) +{ + return buildConstantColumnMaybeNullFromValStr(item, ValStrStdString(item), gwi); +} + +// Create a ConstantColumn for a NOT NULL expression. +// Sets the time zone according to gwi. +execplan::ConstantColumn* buildConstantColumnNotNullUsingValNative(Item* item, gp_walk_info& gwi) +{ + execplan::ConstantColumn* rc = newConstantColumnNotNullUsingValNativeNoTz(item, gwi); + if (rc) + rc->timeZone(gwi.timeZone); + return rc; +} + +/* + Create a ConstantColumn according to cmp_type(). + But do not set the time zone yet. + + Handles NOT NULL values. + + Three ways of value extraction are used depending on the data type: + 1. Using a native val_xxx(). + 2. Using val_str() with further convertion to the native representation. + 3. Using both val_str() and a native val_xxx(). + + We should eventually get rid of N2 and N3 and use N1 for all data types: + - N2 contains a redundant code for str->native conversion. + It should be replaced to an existing code (a Type_handler method call?). + - N3 performs double evalation of the value, which may cause + various negative effects (double side effects or double warnings). +*/ +execplan::ConstantColumn* newConstantColumnNotNullUsingValNativeNoTz(Item* item, gp_walk_info& gwi) +{ + DBUG_ASSERT(item->const_item()); + + switch (item->cmp_type()) + { + case INT_RESULT: + { + if (item->unsigned_flag) + return new execplan::ConstantColumnUInt((uint64_t)item->val_uint(), (int8_t)item->decimal_scale(), + (uint8_t)item->decimal_precision()); + ValStrStdString str(item); + DBUG_ASSERT(!str.isNull()); + return new execplan::ConstantColumnSInt(colType_MysqlToIDB(item), str, (int64_t)item->val_int()); + } + case STRING_RESULT: + { + // Special handling for 0xHHHH literals + if (item->type_handler() == &type_handler_hex_hybrid) + return new execplan::ConstantColumn((int64_t)item->val_int(), execplan::ConstantColumn::NUM); + ValStrStdString str(item); + DBUG_ASSERT(!str.isNull()); + return new execplan::ConstantColumnString(str); + } + case REAL_RESULT: + { + ValStrStdString str(item); + DBUG_ASSERT(!str.isNull()); + return new execplan::ConstantColumnReal(colType_MysqlToIDB(item), str, item->val_real()); + } + case DECIMAL_RESULT: + { + ValStrStdString str(item); + DBUG_ASSERT(!str.isNull()); + return buildDecimalColumn(item, str, gwi); + } + case TIME_RESULT: + { + ValStrStdString str(item); + DBUG_ASSERT(!str.isNull()); + return new execplan::ConstantColumnTemporal(colType_MysqlToIDB(item), str); + } + default: + { + gwi.fatalParseError = true; + gwi.parseErrorText = "Unknown item type"; + break; + } + } + + return nullptr; +} + +bool isSupportedAggregateWithOneConstArg(const Item_sum* item, Item** orig_args) +{ + if (item->argument_count() != 1 || !orig_args[0]->const_item()) + return false; + switch (orig_args[0]->cmp_type()) + { + case INT_RESULT: + case STRING_RESULT: + case REAL_RESULT: + case DECIMAL_RESULT: return true; + default: break; + } + return false; +} + +} // namespace cal_impl_if \ No newline at end of file diff --git a/dbcon/mysql/ha_mcs_execplan_helpers.h b/dbcon/mysql/ha_mcs_execplan_helpers.h new file mode 100644 index 000000000..178e3a45c --- /dev/null +++ b/dbcon/mysql/ha_mcs_execplan_helpers.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2025 MariaDB Corporation + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#pragma once + + +#define PREFER_MY_CONFIG_H +#include +#include "idb_mysql.h" + +#include "constantcolumn.h" +#include "ha_mcs_impl_if.h" + +namespace cal_impl_if +{ + +class ValStrStdString : public std::string +{ + bool mIsNull; + + public: + ValStrStdString(Item* item) + { + String val, *str = item->val_str(&val); + mIsNull = (str == nullptr); + DBUG_ASSERT(mIsNull == item->null_value); + if (!mIsNull) + assign(str->ptr(), str->length()); + } + bool isNull() const + { + return mIsNull; + } +}; + +bool nonConstFunc(Item_func* ifp); +// Note: This function might be unused currently but is kept for future compatibility. +execplan::ConstantColumn* buildConstantColumnMaybeNullFromValStr(const Item* item, const ValStrStdString& valStr, + cal_impl_if::gp_walk_info& gwi); +execplan::ConstantColumn* newConstantColumnNotNullUsingValNativeNoTz(Item* item, gp_walk_info& gwi); +bool isSupportedAggregateWithOneConstArg(const Item_sum* item, Item** orig_args); +execplan::ConstantColumn* buildConstantColumnNotNullUsingValNative(Item* item, gp_walk_info& gwi); +execplan::ConstantColumn* buildConstantColumnMaybeNullUsingValStr(Item* item, gp_walk_info& gwi); +} diff --git a/dbcon/mysql/ha_mcs_execplan_parseinfo_bits.h b/dbcon/mysql/ha_mcs_execplan_parseinfo_bits.h new file mode 100644 index 000000000..0ef458b7a --- /dev/null +++ b/dbcon/mysql/ha_mcs_execplan_parseinfo_bits.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2025 MariaDB Corporation + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#pragma once + +#include + +namespace cal_impl_if +{ + const uint64_t AGG_BIT = 0x01; + const uint64_t SUB_BIT = 0x02; + const uint64_t AF_BIT = 0x04; + const uint64_t CORRELATED = 0x08; +} \ No newline at end of file diff --git a/dbcon/mysql/ha_mcs_execplan_walks.cpp b/dbcon/mysql/ha_mcs_execplan_walks.cpp new file mode 100644 index 000000000..55f66e1dc --- /dev/null +++ b/dbcon/mysql/ha_mcs_execplan_walks.cpp @@ -0,0 +1,1636 @@ +/* Copyright (C) 2025 MariaDB Corporation + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "ha_mcs_execplan_helpers.h" +#include "ha_mcs_execplan_parseinfo_bits.h" +#include "ha_mcs_execplan_walks.h" +#include "ha_mcs_impl_if.h" +#include "ha_subquery.h" + +#include "constantcolumn.h" +#include "logicoperator.h" +#include "rowcolumn.h" +#include "simplefilter.h" + +namespace cal_impl_if +{ +// In certain cases, gp_walk is called recursively. When done so, +// we need to bookmark the rcWorkStack for those cases where a constant +// expression such as 1=1 is used in an if statement or function call. +// This is a seriously bad kludge for MariaDB bug 750. +// +// BM => BookMark +// HWM => HighWaterMark +class RecursionCounter +{ + private: + RecursionCounter() + { + } + + public: + RecursionCounter(cal_impl_if::gp_walk_info* gwip) : fgwip(gwip) + { + ++fgwip->recursionLevel; + + if (fgwip->recursionLevel > fgwip->recursionHWM) + { + fgwip->rcBookMarkStack.push(fgwip->rcWorkStack.size()); + fgwip->recursionHWM = fgwip->recursionLevel; + } + } + ~RecursionCounter() + { + --fgwip->recursionLevel; + + if (fgwip->recursionLevel < fgwip->recursionHWM - 1) + { + fgwip->rcBookMarkStack.pop(); + --fgwip->recursionHWM; + } + } + + cal_impl_if::gp_walk_info* fgwip; +}; + +bool isSecondArgumentConstItem(Item_func* ifp) +{ + return (ifp->argument_count() == 2 && ifp->arguments()[1]->type() == Item::CONST_ITEM); +} + +// SELECT ... WHERE NOT IN (SELECT ); +bool isNotFuncAndConstScalarSubSelect(Item_func* ifp, const std::string& funcName) +{ + return (ifp->with_subquery() && funcName == "not" && ifp->argument_count() == 1 && + ifp->arguments()[0]->type() == Item::FUNC_ITEM && + std::string(((Item_func*)ifp->arguments()[0])->func_name()) == "=" && + isSecondArgumentConstItem((Item_func*)ifp->arguments()[0])); +} + +void gp_walk(const Item* item, void* arg) +{ + cal_impl_if::gp_walk_info* gwip = static_cast(arg); + idbassert(gwip); + + // Bailout... + if (gwip->fatalParseError) + return; + + RecursionCounter r(gwip); // Increments and auto-decrements upon exit. + + Item::Type itype = item->type(); + + // Allow to process XOR(which is Item_func) like other logical operators (which are Item_cond) + if (itype == Item::FUNC_ITEM && ((Item_func*)item)->functype() == Item_func::XOR_FUNC) + itype = Item::COND_ITEM; + + switch (itype) + { + case Item::CACHE_ITEM: + { + // The item or condition is cached as per MariaDB server view but + // for InfiniDB it need to be parsed and executed. + // MCOL-1188 and MCOL-1029 + Item* orig_item = ((Item_cache*)item)->get_example(); + orig_item->traverse_cond(gp_walk, gwip, Item::POSTFIX); + break; + } + case Item::FIELD_ITEM: + { + Item_field* ifp = (Item_field*)item; + + if (ifp) + { + // XXX: this looks awfuly wrong. + execplan::SimpleColumn* scp = buildSimpleColumn(ifp, *gwip); + + if (!scp) + break; + + string aliasTableName(scp->tableAlias()); + scp->tableAlias(aliasTableName); + gwip->rcWorkStack.push(scp->clone()); + boost::shared_ptr scsp(scp); + gwip->scsp = scsp; + + gwip->funcName.clear(); + gwip->columnMap.insert( + execplan::CalpontSelectExecutionPlan::ColumnMap::value_type(string(ifp->field_name.str), scsp)); + + //@bug4636 take where clause column as dummy projection column, but only on local column. + // varbinary aggregate is not supported yet, so rule it out + if (!((scp->joinInfo() & execplan::JOIN_CORRELATED) || + scp->colType().colDataType == execplan::CalpontSystemCatalog::VARBINARY)) + { + TABLE_LIST* tmp = (ifp->cached_table ? ifp->cached_table : 0); + gwip->tableMap[execplan::make_aliastable(scp->schemaName(), scp->tableName(), scp->tableAlias(), + scp->isColumnStore())] = make_pair(1, tmp); + } + } + + break; + } + + case Item::CONST_ITEM: + { + switch (item->cmp_type()) + { + case INT_RESULT: + { + Item* non_const_item = const_cast(item); + gwip->rcWorkStack.push(buildReturnedColumn(non_const_item, *gwip, gwip->fatalParseError)); + break; + } + + case STRING_RESULT: + { + // Special handling for 0xHHHH literals + if (item->type_handler() == &type_handler_hex_hybrid) + { + Item_hex_hybrid* hip = static_cast(const_cast(item)); + gwip->rcWorkStack.push(new execplan::ConstantColumn((int64_t)hip->val_int(), execplan::ConstantColumn::NUM)); + execplan::ConstantColumn* cc = dynamic_cast(gwip->rcWorkStack.top()); + cc->timeZone(gwip->timeZone); + break; + } + + if (item->result_type() == STRING_RESULT) + { + // dangerous cast here + Item* isp = const_cast(item); + String val, *str = isp->val_str(&val); + if (str) + { + string cval; + + if (str->ptr()) + { + cval.assign(str->ptr(), str->length()); + } + + gwip->rcWorkStack.push(new execplan::ConstantColumn(cval)); + (dynamic_cast(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone); + break; + } + else + { + gwip->rcWorkStack.push(new execplan::ConstantColumn("", execplan::ConstantColumn::NULLDATA)); + (dynamic_cast(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone); + break; + } + + gwip->rcWorkStack.push(buildReturnedColumn(isp, *gwip, gwip->fatalParseError)); + } + break; + } + + case REAL_RESULT: + case DECIMAL_RESULT: + case TIME_RESULT: + { + Item* nonConstItem = const_cast(item); + gwip->rcWorkStack.push(buildReturnedColumn(nonConstItem, *gwip, gwip->fatalParseError)); + break; + } + + default: + { + if (gwip->condPush) + { + // push noop for unhandled item + execplan::SimpleColumn* rc = new execplan::SimpleColumn("noop"); + rc->timeZone(gwip->timeZone); + gwip->rcWorkStack.push(rc); + break; + } + + ostringstream oss; + oss << "Unhandled Item type(): " << item->type(); + gwip->parseErrorText = oss.str(); + gwip->fatalParseError = true; + break; + } + } + break; + } + case Item::NULL_ITEM: + { + if (gwip->condPush) + { + // push noop for unhandled item + execplan::SimpleColumn* rc = new execplan::SimpleColumn("noop"); + rc->timeZone(gwip->timeZone); + gwip->rcWorkStack.push(rc); + break; + } + + gwip->rcWorkStack.push(new execplan::ConstantColumn("", execplan::ConstantColumn::NULLDATA)); + (dynamic_cast(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone); + break; + } + + case Item::FUNC_ITEM: + { + Item* ncitem = const_cast(item); + Item_func* ifp = static_cast(ncitem); + + string funcName = ifp->func_name(); + + if (!gwip->condPush) + { + if (!ifp->fixed()) + { + ifp->fix_fields(gwip->thd, &ncitem); + } + + // Special handling for queries of the form: + // SELECT ... WHERE col1 NOT IN (SELECT ); + if (isNotFuncAndConstScalarSubSelect(ifp, funcName)) + { + idbassert(!gwip->ptWorkStack.empty()); + execplan::ParseTree* pt = gwip->ptWorkStack.top(); + execplan::SimpleFilter* sf = dynamic_cast(pt->data()); + + if (sf) + { + boost::shared_ptr sop(new execplan::PredicateOperator("<>")); + sf->op(sop); + return; + } + } + + // Do not call buildSubselectFunc() if the subquery is a const scalar + // subselect of the form: + // (SELECT ) + // As an example: SELECT col1 FROM t1 WHERE col2 = (SELECT 2); + if ((ifp->with_subquery() && !isSecondArgumentConstItem(ifp)) || funcName == "") + { + buildSubselectFunc(ifp, gwip); + return; + } + + if (ifp->argument_count() > 0 && ifp->arguments()) + { + for (uint32_t i = 0; i < ifp->argument_count(); i++) + { + if (ifp->arguments()[i]->type() == Item::SUBSELECT_ITEM) + { + // This is probably NOT IN subquery with derived table in it. + // for some reason, MySQL has not fully optimized the plan at this point. + // noop here, and eventually MySQL will continue its optimization and get + // to rnd_init again. + if (ifp->functype() == Item_func::NOT_FUNC) + return; + + buildSubselectFunc(ifp, gwip); + return; + } + } + } + + if (ifp->functype() == Item_func::TRIG_COND_FUNC && gwip->subQuery) + { + gwip->subQuery->handleFunc(gwip, ifp); + break; + } + + // having clause null function added by MySQL + if (ifp->functype() == Item_func::ISNOTNULLTEST_FUNC) + { + // @bug 4215. remove the argument in rcWorkStack. + if (!gwip->rcWorkStack.empty()) + { + delete gwip->rcWorkStack.top(); + gwip->rcWorkStack.pop(); + } + + break; + } + } + + // try to evaluate const F&E + vector tmpVec; + uint16_t parseInfo = 0; + parse_item(ifp, tmpVec, gwip->fatalParseError, parseInfo, gwip); + + // table mode takes only one table filter + if (gwip->condPush) + { + set tableSet; + + for (uint32_t i = 0; i < tmpVec.size(); i++) + { + if (tmpVec[i]->table_name.str) + tableSet.insert(tmpVec[i]->table_name.str); + } + + if (tableSet.size() > 1) + break; + } + + if (!gwip->fatalParseError && !(parseInfo & AGG_BIT) && !(parseInfo & SUB_BIT) && !nonConstFunc(ifp) && + !(parseInfo & AF_BIT) && tmpVec.size() == 0 && ifp->functype() != Item_func::MULT_EQUAL_FUNC) + { + ValStrStdString valStr(ifp); + + execplan::ConstantColumn* cc = buildConstantColumnMaybeNullFromValStr(ifp, valStr, *gwip); + + for (uint32_t i = 0; i < ifp->argument_count() && !gwip->rcWorkStack.empty(); i++) + { + delete gwip->rcWorkStack.top(); + gwip->rcWorkStack.pop(); + } + + // bug 3137. If filter constant like 1=0, put it to ptWorkStack + // MariaDB bug 750. Breaks if compare is an argument to a function. + // if ((int32_t)gwip->rcWorkStack.size() <= + //(gwip->rcBookMarkStack.empty() + //? + // 0 + //: gwip->rcBookMarkStack.top()) + // && isPredicateFunction(ifp, gwip)) + if (isPredicateFunction(ifp, gwip)) + gwip->ptWorkStack.push(new execplan::ParseTree(cc)); + else + gwip->rcWorkStack.push(cc); + + if (!valStr.isNull()) + IDEBUG(cerr << "Const F&E " << item->full_name() << " evaluate: " << valStr << endl); + + break; + } + + execplan::ReturnedColumn* rc = NULL; + + // @bug4488. Process function for table mode also, not just vtable mode. + rc = buildFunctionColumn(ifp, *gwip, gwip->fatalParseError); + + if (gwip->fatalParseError) + { + if (gwip->clauseType == SELECT) + return; + + // @bug 2585 + if (gwip->parseErrorText.empty()) + { + logging::Message::Args args; + args.add(funcName); + gwip->parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_NON_SUPPORTED_FUNCTION, args); + } + + return; + } + + // predicate operators fall in the old path + if (rc) + { + // @bug 2383. For some reason func_name() for "in" gives " IN " always + if (funcName == "between" || funcName == "in" || funcName == " IN ") + gwip->ptWorkStack.push(new execplan::ParseTree(rc)); + else + gwip->rcWorkStack.push(rc); + } + else + { + // push to pt or rc stack is handled inside the function + buildPredicateItem(ifp, gwip); + } + + break; + } + + case Item::SUM_FUNC_ITEM: + { + Item_sum* isp = (Item_sum*)item; + execplan::ReturnedColumn* rc = buildAggregateColumn(isp, *gwip); + + if (rc) + gwip->rcWorkStack.push(rc); + + break; + } + + case Item::COND_ITEM: + { + // All logical functions are handled here, most of them are Item_cond, + // but XOR (it is Item_func_boolean2) + Item_func* func = (Item_func*)item; + + enum Item_func::Functype ftype = func->functype(); + bool isOr = (ftype == Item_func::COND_OR_FUNC); + bool isXor = (ftype == Item_func::XOR_FUNC); + + List* argumentList; + List xorArgumentList; + + if (isXor) + { + for (unsigned i = 0; i < func->argument_count(); i++) + { + xorArgumentList.push_back(func->arguments()[i]); + } + + argumentList = &xorArgumentList; + } + else + { + argumentList = ((Item_cond*)item)->argument_list(); + } + + // @bug2932. if ptWorkStack contains less items than the condition's arguments, + // the missing one should be in the rcWorkStack, unless the it's subselect. + // @todo need to figure out a way to combine these two stacks while walking. + // if (gwip->ptWorkStack.size() < icp->argument_list()->elements) + { + List_iterator_fast li(*argumentList); + + while (Item* it = li++) + { + //@bug3495, @bug5865 error out non-supported OR with correlated subquery + if (isOr) + { + vector fieldVec; + uint16_t parseInfo = 0; + parse_item(it, fieldVec, gwip->fatalParseError, parseInfo, gwip); + + if (parseInfo & CORRELATED) + { + gwip->fatalParseError = true; + gwip->parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_CORRELATED_SUB_OR); + return; + } + } + + if ((it->type() == Item::FIELD_ITEM || + (it->type() == Item::CONST_ITEM && + (it->cmp_type() == INT_RESULT || it->cmp_type() == DECIMAL_RESULT || + it->cmp_type() == STRING_RESULT || it->cmp_type() == REAL_RESULT)) || + it->type() == Item::NULL_ITEM || + (it->type() == Item::FUNC_ITEM && !isPredicateFunction(it, gwip))) && + !gwip->rcWorkStack.empty()) + { + gwip->ptWorkStack.push(new execplan::ParseTree(gwip->rcWorkStack.top())); + gwip->rcWorkStack.pop(); + } + } + } + + // @bug1603. MySQL's filter tree is a multi-tree grouped by operator. So more than + // two filters saved on the stack so far might belong to this operator. + uint32_t leftInStack = gwip->ptWorkStack.size() - argumentList->elements + 1; + + while (true) + { + if (gwip->ptWorkStack.size() < 2) + break; + + execplan::ParseTree* lhs = gwip->ptWorkStack.top(); + gwip->ptWorkStack.pop(); + execplan::SimpleFilter* lsf = dynamic_cast(lhs->data()); + + if (lsf && lsf->op()->data() == "noop") + { + if (isOr) + { + gwip->parseErrorText = "Unhandled item in WHERE or HAVING clause"; + gwip->fatalParseError = true; + break; + } + else + continue; + } + + execplan::ParseTree* rhs = gwip->ptWorkStack.top(); + gwip->ptWorkStack.pop(); + execplan::SimpleFilter* rsf = dynamic_cast(rhs->data()); + + if (rsf && rsf->op()->data() == "noop") + { + if (isOr) + { + gwip->parseErrorText = "Unhandled item in WHERE or HAVING clause"; + gwip->fatalParseError = true; + break; + } + else + { + delete rhs; + gwip->ptWorkStack.push(lhs); + continue; + } + } + + execplan::Operator* op = new execplan::LogicOperator(func->func_name()); + execplan::ParseTree* ptp = new execplan::ParseTree(op); + ptp->left(lhs); + ptp->right(rhs); + gwip->ptWorkStack.push(ptp); + + if (gwip->ptWorkStack.size() == leftInStack) + break; + } + + // special handling for subquery with aggregate. MySQL adds isnull function to the selected + // column. InfiniDB will remove it and set nullmatch flag if it's NOT_IN sub. + // @todo need more checking here to make sure it's not a user input OR operator + if (isOr && gwip->subQuery) + gwip->subQuery->handleFunc(gwip, func); + + break; + } + + case Item::REF_ITEM: + { + Item* col = *(((Item_ref*)item)->ref); + execplan::ReturnedColumn* rc = NULL; + // ref item is not pre-walked. force clause type to SELECT + ClauseType clauseType = gwip->clauseType; + gwip->clauseType = SELECT; + + if (col->type() != Item::COND_ITEM) + { + rc = buildReturnedColumn(col, *gwip, gwip->fatalParseError, true); + + if (col->type() == Item::FIELD_ITEM) + gwip->fatalParseError = false; + } + + execplan::SimpleColumn* sc = clauseType == HAVING ? nullptr : dynamic_cast(rc); + + if (sc) + { + boost::shared_ptr scsp(sc->clone()); + gwip->scsp = scsp; + + if (col->type() == Item::FIELD_ITEM) + { + const Item_ident* ident_field = dynamic_cast(item); + if (ident_field) + { + const auto& field_name = string(ident_field->field_name.str); + auto colMap = execplan::CalpontSelectExecutionPlan::ColumnMap::value_type(field_name, scsp); + gwip->columnMap.insert(colMap); + } + } + } + + bool cando = true; + gwip->clauseType = clauseType; + + if (rc) + { + if (((Item_ref*)item)->depended_from) + { + rc->joinInfo(rc->joinInfo() | execplan::JOIN_CORRELATED); + + if (gwip->subQuery) + gwip->subQuery->correlated(true); + + execplan::SimpleColumn* scp = dynamic_cast(rc); + + if (scp) + gwip->correlatedTbNameVec.push_back( + execplan::make_aliastable(scp->schemaName(), scp->tableName(), scp->tableAlias())); + + if (gwip->subSelectType == execplan::CalpontSelectExecutionPlan::SINGLEROW_SUBS) + rc->joinInfo(rc->joinInfo() | execplan::JOIN_SCALAR | execplan::JOIN_SEMI); + + if (gwip->subSelectType == execplan::CalpontSelectExecutionPlan::SELECT_SUBS) + rc->joinInfo(rc->joinInfo() | execplan::JOIN_SCALAR | execplan::JOIN_OUTER_SELECT); + } + + gwip->rcWorkStack.push(rc); + } + else if (col->type() == Item::FUNC_ITEM) + { + // sometimes mysql treat having filter items inconsistently. In such cases, + // which are always predicate operator, the function (gp_key>3) comes in as + // one item. + Item_func* ifp = (Item_func*)col; + + for (uint32_t i = 0; i < ifp->argument_count(); i++) + { + execplan::ReturnedColumn* operand = NULL; + + if (ifp->arguments()[i]->type() == Item::REF_ITEM) + { + Item* op = *(((Item_ref*)ifp->arguments()[i])->ref); + operand = buildReturnedColumn(op, *gwip, gwip->fatalParseError); + } + else + operand = buildReturnedColumn(ifp->arguments()[i], *gwip, gwip->fatalParseError); + + if (operand) + { + gwip->rcWorkStack.push(operand); + if (i == 0 && gwip->scsp == NULL) // first item is the WHEN LHS + { + execplan::SimpleColumn* sc = dynamic_cast(operand); + if (sc) + { + gwip->scsp.reset(sc->clone()); // We need to clone else sc gets double deleted. This code is + // rarely executed so the cost is acceptable. + } + } + } + else + { + cando = false; + break; + } + } + + if (cando) + buildPredicateItem(ifp, gwip); + } + else if (col->type() == Item::COND_ITEM) + { + gwip->ptWorkStack.push(buildParseTree(col, *gwip, gwip->fatalParseError)); + } + else if (col->type() == Item::FIELD_ITEM && gwip->clauseType == HAVING) + { + // ReturnedColumn* rc = buildAggFrmTempField(const_cast(item), *gwip); + execplan::ReturnedColumn* rc = buildReturnedColumn(const_cast(item), *gwip, gwip->fatalParseError); + if (rc) + gwip->rcWorkStack.push(rc); + + break; + } + else + { + cando = false; + } + + execplan::SimpleColumn* thisSC = dynamic_cast(rc); + if (thisSC) + { + gwip->scsp.reset(thisSC->clone()); + } + if (!rc && !cando) + { + ostringstream oss; + oss << "Unhandled Item type(): " << item->type(); + gwip->parseErrorText = oss.str(); + gwip->fatalParseError = true; + } + + break; + } + + case Item::SUBSELECT_ITEM: + { + if (gwip->condPush) // table mode + break; + + Item_subselect* sub = (Item_subselect*)item; + + if (sub->substype() == Item_subselect::EXISTS_SUBS) + { + SubQuery* orig = gwip->subQuery; + ExistsSub* existsSub = new ExistsSub(*gwip, sub); + gwip->hasSubSelect = true; + gwip->subQuery = existsSub; + gwip->ptWorkStack.push(existsSub->transform()); + // recover original + gwip->subQuery = orig; + gwip->lastSub = existsSub; + } + else if (sub->substype() == Item_subselect::IN_SUBS) + { + if (!((Item_in_subselect*)sub)->optimizer && gwip->thd->derived_tables_processing) + { + ostringstream oss; + oss << "Invalid In_optimizer: " << item->type(); + gwip->parseErrorText = oss.str(); + gwip->fatalParseError = true; + break; + } + } + + // store a dummy subselect object. the transform is handled in item_func. + execplan::SubSelect* subselect = new execplan::SubSelect(); + gwip->rcWorkStack.push(subselect); + break; + } + + case Item::ROW_ITEM: + { + Item_row* row = (Item_row*)item; + execplan::RowColumn* rowCol = new execplan::RowColumn(); + vector cols; + // temp change clause type because the elements of row column are not walked yet + gwip->clauseType = SELECT; + for (uint32_t i = 0; i < row->cols(); i++) + cols.push_back(execplan::SRCP(buildReturnedColumn(row->element_index(i), *gwip, gwip->fatalParseError))); + + gwip->clauseType = WHERE; + rowCol->columnVec(cols); + gwip->rcWorkStack.push(rowCol); + break; + } + + case Item::EXPR_CACHE_ITEM: + { + ((Item_cache_wrapper*)item)->get_orig_item()->traverse_cond(gp_walk, arg, Item::POSTFIX); + break; + } + + case Item::WINDOW_FUNC_ITEM: + { + gwip->hasWindowFunc = true; + Item_window_func* ifa = (Item_window_func*)item; + execplan::ReturnedColumn* af = buildWindowFunctionColumn(ifa, *gwip, gwip->fatalParseError); + + if (af) + gwip->rcWorkStack.push(af); + + break; + } + + case Item::COPY_STR_ITEM: printf("********** received COPY_STR_ITEM *********\n"); break; + + case Item::FIELD_AVG_ITEM: printf("********** received FIELD_AVG_ITEM *********\n"); break; + + case Item::DEFAULT_VALUE_ITEM: printf("********** received DEFAULT_VALUE_ITEM *********\n"); break; + + case Item::PROC_ITEM: printf("********** received PROC_ITEM *********\n"); break; + + case Item::FIELD_STD_ITEM: printf("********** received FIELD_STD_ITEM *********\n"); break; + + case Item::FIELD_VARIANCE_ITEM: printf("********** received FIELD_VARIANCE_ITEM *********\n"); break; + + case Item::INSERT_VALUE_ITEM: printf("********** received INSERT_VALUE_ITEM *********\n"); break; + + case Item::PARAM_ITEM: printf("********** received PARAM_ITEM *********\n"); break; + + case Item::TRIGGER_FIELD_ITEM: printf("********** received TRIGGER_FIELD_ITEM *********\n"); break; + + case Item::TYPE_HOLDER: std::cerr << "********** received TYPE_HOLDER *********" << std::endl; break; + default: + { + if (gwip->condPush) + { + // push noop for unhandled item + execplan::SimpleColumn* rc = new execplan::SimpleColumn("noop"); + rc->timeZone(gwip->timeZone); + gwip->rcWorkStack.push(rc); + break; + } + + ostringstream oss; + oss << "Unhandled Item type (2): " << item->type(); + gwip->parseErrorText = oss.str(); + gwip->fatalParseError = true; + break; + } + } + + return; +} + +/** @info this function recursivly walks an item's arguments and push all + * the involved item_fields to the passed in vector. It's used in parsing + * functions or arithmetic expressions for vtable post process. + */ +void parse_item(Item* item, vector& field_vec, bool& hasNonSupportItem, uint16_t& parseInfo, + gp_walk_info* gwi) +{ + Item::Type itype = item->type(); + + switch (itype) + { + case Item::FIELD_ITEM: + { + Item_field* ifp = static_cast(item); + field_vec.push_back(ifp); + return; + } + + case Item::SUM_FUNC_ITEM: + { + // hasAggColumn = true; + parseInfo |= AGG_BIT; + Item_sum* isp = static_cast(item); + Item** sfitempp = isp->arguments(); + + for (uint32_t i = 0; i < isp->argument_count(); i++) + parse_item(sfitempp[i], field_vec, hasNonSupportItem, parseInfo, gwi); + + break; + } + + case Item::FUNC_ITEM: + { + Item_func* isp = static_cast(item); + + if (string(isp->func_name()) == "") + { + parseInfo |= SUB_BIT; + parseInfo |= CORRELATED; + break; + } + + for (uint32_t i = 0; i < isp->argument_count(); i++) + parse_item(isp->arguments()[i], field_vec, hasNonSupportItem, parseInfo, gwi); + + break; + } + + case Item::COND_ITEM: + { + Item_cond* icp = static_cast(item); + List_iterator_fast it(*(icp->argument_list())); + Item* cond_item; + + while ((cond_item = it++)) + parse_item(cond_item, field_vec, hasNonSupportItem, parseInfo, gwi); + + break; + } + + case Item::REF_ITEM: + { + Item_ref* ref = (Item_ref*)item; + if (ref->ref_type() == Item_ref::DIRECT_REF) + { + parse_item(ref->real_item(), field_vec, hasNonSupportItem, parseInfo, gwi); + break; + } + while (true) + { + ref = (Item_ref*)item; + if ((*(ref->ref))->type() == Item::SUM_FUNC_ITEM) + { + parseInfo |= AGG_BIT; + Item_sum* isp = static_cast(*(ref->ref)); + Item** sfitempp = isp->arguments(); + + // special handling for count(*). This should not be treated as constant. + if (isSupportedAggregateWithOneConstArg(isp, sfitempp)) + { + field_vec.push_back(nullptr); // dummy + } + + for (uint32_t i = 0; i < isp->argument_count(); i++) + parse_item(sfitempp[i], field_vec, hasNonSupportItem, parseInfo, gwi); + + break; + } + else if ((*(ref->ref))->type() == Item::FIELD_ITEM) + { + // MCOL-1510. This could be a non-supported function + // argument in form of a temp_table_field, so check + // and set hasNonSupportItem if it is so. + // ReturnedColumn* rc = NULL; + // if (gwi) + // rc = buildAggFrmTempField(ref, *gwi); + + // if (!rc) + //{ + Item_field* ifp = static_cast(*(ref->ref)); + field_vec.push_back(ifp); + //} + break; + } + else if ((*(ref->ref))->type() == Item::FUNC_ITEM) + { + Item_func* isp = static_cast(*(ref->ref)); + Item** sfitempp = isp->arguments(); + + for (uint32_t i = 0; i < isp->argument_count(); i++) + parse_item(sfitempp[i], field_vec, hasNonSupportItem, parseInfo, gwi); + + break; + } + else if ((*(ref->ref))->type() == Item::CACHE_ITEM) + { + Item_cache* isp = static_cast(*(ref->ref)); + parse_item(isp->get_example(), field_vec, hasNonSupportItem, parseInfo, gwi); + break; + } + else if ((*(ref->ref))->type() == Item::REF_ITEM) + { + item = (*(ref->ref)); + continue; + } + else if ((*(ref->ref))->type() == Item::WINDOW_FUNC_ITEM) + { + parseInfo |= AF_BIT; + break; + } + else + { + cerr << "UNKNOWN REF Item" << endl; + break; + } + } + + break; + } + + case Item::SUBSELECT_ITEM: + { + parseInfo |= SUB_BIT; + Item_subselect* sub = (Item_subselect*)item; + + if (sub->is_correlated) + parseInfo |= CORRELATED; + + break; + } + + case Item::ROW_ITEM: + { + Item_row* row = (Item_row*)item; + + for (uint32_t i = 0; i < row->cols(); i++) + parse_item(row->element_index(i), field_vec, hasNonSupportItem, parseInfo, gwi); + + break; + } + + case Item::EXPR_CACHE_ITEM: + { + // item is a Item_cache_wrapper. Shouldn't get here. + // DRRTUY TODO Why + IDEBUG(std::cerr << "EXPR_CACHE_ITEM in parse_item\n" << std::endl); + gwi->fatalParseError = true; + // DRRTUY The questionable error text. I've seen + // ERR_CORRELATED_SUB_OR + string parseErrorText = logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_NON_SUPPORT_SUB_QUERY_TYPE); + setError(gwi->thd, ER_CHECK_NOT_IMPLEMENTED, parseErrorText); + break; + } + + case Item::WINDOW_FUNC_ITEM: parseInfo |= AF_BIT; break; + + default: break; + } +} + +void debug_walk(const Item* item, void* arg) +{ + switch (item->type()) + { + case Item::FIELD_ITEM: + { + Item_field* ifp = (Item_field*)item; + cerr << "FIELD_ITEM: " << (ifp->db_name.str ? ifp->db_name.str : "") << '.' << bestTableName(ifp) << '.' + << ifp->field_name.str << endl; + break; + } + case Item::CONST_ITEM: + { + switch (item->cmp_type()) + { + case INT_RESULT: + { + Item_int* iip = (Item_int*)item; + cerr << "INT_ITEM: "; + + if (iip->name.length) + cerr << iip->name.str << " (from name string)" << endl; + else + cerr << iip->val_int() << endl; + + break; + } + case STRING_RESULT: + { + Item_string* isp = (Item_string*)item; + String val, *str = isp->val_str(&val); + string valStr; + valStr.assign(str->ptr(), str->length()); + cerr << "STRING_ITEM: >" << valStr << '<' << endl; + break; + } + case REAL_RESULT: + { + cerr << "REAL_ITEM" << endl; + break; + } + case DECIMAL_RESULT: + { + cerr << "DECIMAL_ITEM" << endl; + break; + } + case TIME_RESULT: + { + String val, *str = NULL; + Item_temporal_literal* itp = (Item_temporal_literal*)item; + str = itp->val_str(&val); + cerr << "DATE ITEM: "; + + if (str) + cerr << ": (" << str->ptr() << ')' << endl; + else + cerr << ": " << endl; + + break; + } + default: + { + cerr << ": Unknown cmp_type" << endl; + break; + } + } + break; + } + case Item::FUNC_ITEM: + { + Item_func* ifp = (Item_func*)item; + Item_func_opt_neg* inp; + cerr << "FUNC_ITEM: "; + + switch (ifp->functype()) + { + case Item_func::UNKNOWN_FUNC: // 0 + cerr << ifp->func_name() << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::GT_FUNC: // 7 + cerr << '>' << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::EQ_FUNC: // 1 + cerr << '=' << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::GE_FUNC: cerr << ">=" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::LE_FUNC: cerr << "<=" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::LT_FUNC: cerr << '<' << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::NE_FUNC: cerr << "<>" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::NEG_FUNC: // 45 + cerr << "unary minus" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::IN_FUNC: // 16 + inp = (Item_func_opt_neg*)ifp; + + if (inp->negated) + cerr << "not "; + + cerr << "in" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::BETWEEN: + inp = (Item_func_opt_neg*)ifp; + + if (inp->negated) + cerr << "not "; + + cerr << "between" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::ISNULL_FUNC: // 10 + cerr << "is null" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::ISNOTNULL_FUNC: // 11 + cerr << "is not null" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::NOT_ALL_FUNC: cerr << "not_all" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::NOT_FUNC: cerr << "not_func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::TRIG_COND_FUNC: + cerr << "trig_cond_func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::ISNOTNULLTEST_FUNC: + cerr << "isnotnulltest_func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::MULT_EQUAL_FUNC: + { + cerr << "mult_equal_func:" << " (" << ifp->functype() << ")" << endl; + Item_equal* item_eq = (Item_equal*)ifp; + Item_equal_fields_iterator it(*item_eq); + Item* item; + + while ((item = it++)) + { + Field* equal_field = it.get_curr_field(); + cerr << equal_field->field_name.str << endl; + } + + break; + } + + case Item_func::EQUAL_FUNC: cerr << "equal func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::FT_FUNC: cerr << "ft func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::LIKE_FUNC: cerr << "like func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::COND_AND_FUNC: + cerr << "cond and func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::COND_OR_FUNC: cerr << "cond or func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::XOR_FUNC: cerr << "xor func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::INTERVAL_FUNC: + cerr << "interval func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_EQUALS_FUNC: + cerr << "sp equals func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_DISJOINT_FUNC: + cerr << "sp disjoint func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_INTERSECTS_FUNC: + cerr << "sp intersects func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_TOUCHES_FUNC: + cerr << "sp touches func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_CROSSES_FUNC: + cerr << "sp crosses func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_WITHIN_FUNC: + cerr << "sp within func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_CONTAINS_FUNC: + cerr << "sp contains func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_OVERLAPS_FUNC: + cerr << "sp overlaps func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_STARTPOINT: + cerr << "sp startpoint func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_ENDPOINT: + cerr << "sp endpoint func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_EXTERIORRING: + cerr << "sp exteriorring func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_POINTN: cerr << "sp pointn func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::SP_GEOMETRYN: + cerr << "sp geometryn func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_INTERIORRINGN: + cerr << "sp exteriorringn func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::SP_RELATE_FUNC: + cerr << "sp relate func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::NOW_FUNC: cerr << "now func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::SUSERVAR_FUNC: + cerr << "suservar func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::GUSERVAR_FUNC: + cerr << "guservar func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::COLLATE_FUNC: cerr << "collate func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::EXTRACT_FUNC: cerr << "extract func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::CHAR_TYPECAST_FUNC: + cerr << "char typecast func" << " (" << ifp->functype() << ")" << endl; + break; + + case Item_func::FUNC_SP: cerr << "func sp func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::UDF_FUNC: cerr << "udf func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::GSYSVAR_FUNC: cerr << "gsysvar func" << " (" << ifp->functype() << ")" << endl; break; + + case Item_func::DYNCOL_FUNC: cerr << "dyncol func" << " (" << ifp->functype() << ")" << endl; break; + + default: cerr << "type=" << ifp->functype() << endl; break; + } + + break; + } + + case Item::COND_ITEM: + { + Item_cond* icp = (Item_cond*)item; + cerr << "COND_ITEM: " << icp->func_name() << endl; + break; + } + + case Item::SUM_FUNC_ITEM: + { + Item_sum* isp = (Item_sum*)item; + char* item_name = const_cast(item->name.str); + + // MCOL-1052 This is an extended SELECT list item + if (!item_name && isp->get_arg_count() && isp->get_arg(0)->name.length) + { + item_name = const_cast(isp->get_arg(0)->name.str); + } + else if (!item_name && isp->get_arg_count() && isp->get_arg(0)->type() == Item::CONST_ITEM && + isp->get_arg(0)->cmp_type() == INT_RESULT) + { + item_name = (char*)"INT||*"; + } + else if (!item_name) + { + item_name = (char*)""; + } + + switch (isp->sum_func()) + { + case Item_sum::SUM_FUNC: cerr << "SUM_FUNC: " << item_name << endl; break; + + case Item_sum::SUM_DISTINCT_FUNC: cerr << "SUM_DISTINCT_FUNC: " << item_name << endl; break; + + case Item_sum::AVG_FUNC: cerr << "AVG_FUNC: " << item_name << endl; break; + + case Item_sum::COUNT_FUNC: cerr << "COUNT_FUNC: " << item_name << endl; break; + + case Item_sum::COUNT_DISTINCT_FUNC: cerr << "COUNT_DISTINCT_FUNC: " << item_name << endl; break; + + case Item_sum::MIN_FUNC: cerr << "MIN_FUNC: " << item_name << endl; break; + + case Item_sum::MAX_FUNC: cerr << "MAX_FUNC: " << item_name << endl; break; + + case Item_sum::UDF_SUM_FUNC: cerr << "UDAF_FUNC: " << item_name << endl; break; + + default: cerr << "SUM_FUNC_ITEM type=" << isp->sum_func() << endl; break; + } + + break; + } + + case Item::SUBSELECT_ITEM: + { + Item_subselect* sub = (Item_subselect*)item; + cerr << "SUBSELECT Item: "; + + switch (sub->substype()) + { + case Item_subselect::EXISTS_SUBS: cerr << "EXISTS"; break; + + case Item_subselect::IN_SUBS: cerr << "IN"; break; + + default: cerr << sub->substype(); break; + } + + cerr << endl; + JOIN* join = sub->get_select_lex()->join; + + if (join) + { + Item_cond* cond = static_cast(join->conds); + + if (cond) + cond->traverse_cond(debug_walk, arg, Item::POSTFIX); + } + + cerr << "Finish subselect item traversing" << endl; + break; + } + + case Item::REF_ITEM: + { + Item_ref* ref = (Item_ref*)item; + + if (ref->real_item()->type() == Item::CACHE_ITEM) + { + Item* field = ((Item_cache*)ref->real_item())->get_example(); + + if (field->type() == Item::FIELD_ITEM) + { + Item_field* ifp = (Item_field*)field; + // ifp->cached_table->select_lex->select_number gives the select level. + // could be used on alias. + // could also be used to tell correlated join (equal level). + cerr << "CACHED REF FIELD_ITEM: " << ifp->db_name.str << '.' << bestTableName(ifp) << '.' + << ifp->field_name.str << endl; + break; + } + else if (field->type() == Item::FUNC_ITEM) + { + Item_func* ifp = (Item_func*)field; + cerr << "CACHED REF FUNC_ITEM " << ifp->func_name() << endl; + } + else if (field->type() == Item::REF_ITEM) + { + Item_ref* ifr = (Item_ref*)field; + string refType; + string realType; + + switch (ifr->ref_type()) + { + case Item_ref::REF: refType = "REF"; break; + + case Item_ref::DIRECT_REF: refType = "DIRECT_REF"; break; + + case Item_ref::VIEW_REF: refType = "VIEW_REF"; break; + + case Item_ref::OUTER_REF: refType = "OUTER_REF"; break; + + case Item_ref::AGGREGATE_REF: refType = "AGGREGATE_REF"; break; + + default: refType = "UNKNOWN"; break; + } + + switch (ifr->real_type()) + { + case Item::FIELD_ITEM: + { + Item_field* ifp = (Item_field*)(*(ifr->ref)); + realType = "FIELD_ITEM "; + realType += ifp->db_name.str; + realType += '.'; + realType += bestTableName(ifp); + realType += '.'; + realType += ifp->field_name.str; + break; + } + + case Item::SUM_FUNC_ITEM: + { + Item_sum* isp = (Item_sum*)(*(ifr->ref)); + + if (isp->sum_func() == Item_sum::GROUP_CONCAT_FUNC) + realType = "GROUP_CONCAT_FUNC"; + else + realType = "SUM_FUNC_ITEM"; + + break; + } + + case Item::REF_ITEM: + // Need recursion here + realType = "REF_ITEM"; + break; + + case Item::FUNC_ITEM: + { + Item_func* ifp = (Item_func*)(*(ifr->ref)); + realType = "FUNC_ITEM "; + realType += ifp->func_name(); + break; + } + + default: + { + realType = "UNKNOWN"; + } + } + + cerr << "CACHED REF_ITEM: ref type " << refType.c_str() << " real type " << realType.c_str() + << endl; + break; + } + else + { + cerr << "REF_ITEM with CACHE_ITEM type unknown " << field->type() << endl; + } + } + else if (ref->real_item()->type() == Item::FIELD_ITEM) + { + Item_field* ifp = (Item_field*)ref->real_item(); + + // MCOL-1052 The field referenced presumable came from + // extended SELECT list. + if (!ifp->field_name.str) + { + cerr << "REF extra FIELD_ITEM: " << ifp->name.str << endl; + } + else + { + cerr << "REF FIELD_ITEM: " << ifp->db_name.str << '.' << bestTableName(ifp) << '.' + << ifp->field_name.str << endl; + } + + break; + } + else if (ref->real_item()->type() == Item::FUNC_ITEM) + { + Item_func* ifp = (Item_func*)ref->real_item(); + cerr << "REF FUNC_ITEM " << ifp->func_name() << endl; + } + else if (ref->real_item()->type() == Item::WINDOW_FUNC_ITEM) + { + Item_window_func* ifp = (Item_window_func*)ref->real_item(); + cerr << "REF WINDOW_FUNC_ITEM " << ifp->window_func()->func_name() << endl; + } + else + { + cerr << "UNKNOWN REF ITEM type " << ref->real_item()->type() << endl; + } + + break; + } + + case Item::ROW_ITEM: + { + Item_row* row = (Item_row*)item; + cerr << "ROW_ITEM: " << endl; + + for (uint32_t i = 0; i < row->cols(); i++) + debug_walk(row->element_index(i), 0); + + break; + } + + case Item::EXPR_CACHE_ITEM: + { + cerr << "Expr Cache Item" << endl; + ((Item_cache_wrapper*)item)->get_orig_item()->traverse_cond(debug_walk, arg, Item::POSTFIX); + break; + } + + case Item::CACHE_ITEM: + { + Item_cache* isp = (Item_cache*)item; + // MCOL-46 isp->val_str() can cause a call to execute a subquery. We're not set up + // to execute yet. +#if 0 + + switch (item->result_type()) + { + case STRING_RESULT: + cerr << "CACHE_STRING_ITEM" << endl; + break; + + case REAL_RESULT: + cerr << "CACHE_REAL_ITEM " << isp->val_real() << endl; + break; + + case INT_RESULT: + cerr << "CACHE_INT_ITEM " << isp->val_int() << endl; + break; + + case ROW_RESULT: + cerr << "CACHE_ROW_ITEM" << endl; + break; + + case DECIMAL_RESULT: + cerr << "CACHE_DECIMAL_ITEM " << isp->val_decimal() << endl; + break; + + default: + cerr << "CACHE_UNKNOWN_ITEM" << endl; + break; + } + +#endif + Item* field = isp->get_example(); + + if (field->type() == Item::FIELD_ITEM) + { + Item_field* ifp = (Item_field*)field; + // ifp->cached_table->select_lex->select_number gives the select level. + // could be used on alias. + // could also be used to tell correlated join (equal level). + cerr << "CACHED FIELD_ITEM: " << ifp->db_name.str << '.' << bestTableName(ifp) << '.' + << ifp->field_name.str << endl; + break; + } + else if (field->type() == Item::REF_ITEM) + { + Item_ref* ifr = (Item_ref*)field; + string refType; + string realType; + + switch (ifr->ref_type()) + { + case Item_ref::REF: refType = "REF"; break; + + case Item_ref::DIRECT_REF: refType = "DIRECT_REF"; break; + + case Item_ref::VIEW_REF: refType = "VIEW_REF"; break; + + case Item_ref::OUTER_REF: refType = "OUTER_REF"; break; + + case Item_ref::AGGREGATE_REF: refType = "AGGREGATE_REF"; break; + + default: refType = "UNKNOWN"; break; + } + + switch (ifr->real_type()) + { + case Item::FIELD_ITEM: + { + Item_field* ifp = (Item_field*)(*(ifr->ref)); + realType = "FIELD_ITEM "; + realType += ifp->db_name.str; + realType += '.'; + realType += bestTableName(ifp); + realType += '.'; + realType += ifp->field_name.str; + break; + } + + case Item::SUM_FUNC_ITEM: + { + Item_sum* isp = (Item_sum*)(*(ifr->ref)); + + if (isp->sum_func() == Item_sum::GROUP_CONCAT_FUNC) + realType = "GROUP_CONCAT_FUNC"; + else + realType = "SUM_FUNC_ITEM"; + + break; + } + + case Item::REF_ITEM: + // Need recursion here + realType = "REF_ITEM"; + break; + + case Item::FUNC_ITEM: + { + Item_func* ifp = (Item_func*)(*(ifr->ref)); + realType = "FUNC_ITEM "; + realType += ifp->func_name(); + break; + } + + default: + { + realType = "UNKNOWN"; + } + } + + cerr << "CACHE_ITEM ref type " << refType.c_str() << " real type " << realType.c_str() << endl; + break; + } + else if (field->type() == Item::FUNC_ITEM) + { + Item_func* ifp = (Item_func*)field; + cerr << "CACHE_ITEM FUNC_ITEM " << ifp->func_name() << endl; + break; + } + else + { + cerr << "CACHE_ITEM type unknown " << field->type() << endl; + } + + break; + } + + case Item::WINDOW_FUNC_ITEM: + { + Item_window_func* ifp = (Item_window_func*)item; + cerr << "Window Function Item " << ifp->window_func()->func_name() << endl; + break; + } + + case Item::NULL_ITEM: + { + cerr << "NULL item" << endl; + break; + } + + case Item::TYPE_HOLDER: + { + cerr << "TYPE_HOLDER item with cmp_type " << item->cmp_type() << endl; + break; + } + + default: + { + cerr << "UNKNOWN_ITEM type " << item->type() << endl; + break; + } + } +} + +} \ No newline at end of file diff --git a/dbcon/mysql/ha_mcs_execplan_walks.h b/dbcon/mysql/ha_mcs_execplan_walks.h new file mode 100644 index 000000000..f3efd9e3c --- /dev/null +++ b/dbcon/mysql/ha_mcs_execplan_walks.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2025 MariaDB Corporation + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#pragma once + +#define PREFER_MY_CONFIG_H +#include +#include "idb_mysql.h" + +namespace cal_impl_if +{ +void debug_walk(const Item* item, void* arg); +void gp_walk(const Item* item, void* arg); +} \ No newline at end of file