You've already forked mariadb-columnstore-engine
							
							
				mirror of
				https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
				synced 2025-10-31 18:30:33 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			1682 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1682 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* 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"
 | |
| #include "pseudocolumn.h"
 | |
| 
 | |
| using namespace std;
 | |
| 
 | |
| 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;
 | |
| };
 | |
| 
 | |
| // RAII guard to automatically clean up work stacks on error.
 | |
| // If a fatal parse error occurs, this ensures allocated objects
 | |
| // are deleted when the guard goes out of scope, preventing memory leaks.
 | |
| class CleanupGuard
 | |
| {
 | |
|  private:
 | |
|   CleanupGuard() = delete;
 | |
|   CleanupGuard(const CleanupGuard&) = delete;
 | |
|   CleanupGuard& operator=(const CleanupGuard&) = delete;
 | |
| 
 | |
|  public:
 | |
|   explicit CleanupGuard(cal_impl_if::gp_walk_info* gwip) : fgwip(gwip)
 | |
|   {
 | |
|   }
 | |
|   ~CleanupGuard()
 | |
|   {
 | |
|     if (fgwip->fatalParseError)
 | |
|     {
 | |
|       clearDeleteStacks(*fgwip);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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 <col> NOT IN (SELECT <const_item>);
 | |
| 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<cal_impl_if::gp_walk_info*>(arg);
 | |
|   idbassert(gwip);
 | |
| 
 | |
|   // RAII guard: automatically cleans up work stacks on error when guard goes out of scope
 | |
|   CleanupGuard cleanup(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;
 | |
| 
 | |
|         std::string aliasTableName(scp->tableAlias());
 | |
|         scp->tableAlias(aliasTableName);
 | |
|         gwip->rcWorkStack.push(scp->clone());
 | |
|         boost::shared_ptr<execplan::SimpleColumn> scsp(scp);
 | |
|         gwip->scsp = scsp;
 | |
| 
 | |
|         gwip->funcName.clear();
 | |
|         gwip->columnMap.insert(execplan::CalpontSelectExecutionPlan::ColumnMap::value_type(
 | |
|             std::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())] = std::make_pair(1, tmp);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case Item::CONST_ITEM:
 | |
|     {
 | |
|       switch (item->cmp_type())
 | |
|       {
 | |
|         case INT_RESULT:
 | |
|         {
 | |
|           Item* non_const_item = const_cast<Item*>(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<Item_hex_hybrid*>(const_cast<Item*>(item));
 | |
|             gwip->rcWorkStack.push(
 | |
|                 new execplan::ConstantColumn((int64_t)hip->val_int(), execplan::ConstantColumn::NUM));
 | |
|             execplan::ConstantColumn* cc = dynamic_cast<execplan::ConstantColumn*>(gwip->rcWorkStack.top());
 | |
|             cc->timeZone(gwip->timeZone);
 | |
|             break;
 | |
|           }
 | |
| 
 | |
|           if (item->result_type() == STRING_RESULT)
 | |
|           {
 | |
|             // dangerous cast here
 | |
|             Item* isp = const_cast<Item*>(item);
 | |
|             String val, *str = isp->val_str(&val);
 | |
|             if (str)
 | |
|             {
 | |
|               std::string cval;
 | |
| 
 | |
|               if (str->ptr())
 | |
|               {
 | |
|                 cval.assign(str->ptr(), str->length());
 | |
|               }
 | |
| 
 | |
|               gwip->rcWorkStack.push(new execplan::ConstantColumn(cval));
 | |
|               (dynamic_cast<execplan::ConstantColumn*>(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone);
 | |
|               break;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|               gwip->rcWorkStack.push(new execplan::ConstantColumn("", execplan::ConstantColumn::NULLDATA));
 | |
|               (dynamic_cast<execplan::ConstantColumn*>(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*>(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;
 | |
|           }
 | |
| 
 | |
|           std::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<execplan::ConstantColumn*>(gwip->rcWorkStack.top()))->timeZone(gwip->timeZone);
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case Item::FUNC_ITEM:
 | |
|     {
 | |
|       Item* ncitem = const_cast<Item*>(item);
 | |
|       Item_func* ifp = static_cast<Item_func*>(ncitem);
 | |
|       std::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 <const_item>);
 | |
|         if (isNotFuncAndConstScalarSubSelect(ifp, funcName))
 | |
|         {
 | |
|           idbassert(!gwip->ptWorkStack.empty());
 | |
|           execplan::ParseTree* pt = gwip->ptWorkStack.top();
 | |
|           execplan::SimpleFilter* sf = dynamic_cast<execplan::SimpleFilter*>(pt->data());
 | |
| 
 | |
|           if (sf)
 | |
|           {
 | |
|             boost::shared_ptr<execplan::Operator> sop(new execplan::PredicateOperator("<>"));
 | |
|             sf->op(sop);
 | |
|             return;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // Do not call buildSubselectFunc() if the subquery is a const scalar
 | |
|         // subselect of the form:
 | |
|         // (SELECT <const_item>)
 | |
|         // As an example: SELECT col1 FROM t1 WHERE col2 = (SELECT 2);
 | |
|         if ((ifp->with_subquery() && !isSecondArgumentConstItem(ifp)) || funcName == "<in_optimizer>")
 | |
|         {
 | |
|           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
 | |
|       std::vector<Item_field*> tmpVec;
 | |
|       uint16_t parseInfo = 0;
 | |
|       parse_item(ifp, tmpVec, gwip->fatalParseError, parseInfo, gwip);
 | |
| 
 | |
|       // table mode takes only one table filter
 | |
|       if (gwip->condPush)
 | |
|       {
 | |
|         std::set<std::string> 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)
 | |
|       {
 | |
|         if(ifp->functype() == Item_func::UDF_FUNC &&
 | |
|            execplan::PseudoColumn::pseudoNameToType(funcName) != execplan::PSEUDO_UNKNOWN &&
 | |
|            ifp->arguments()[0] != nullptr && ifp->arguments()[0]->type() != Item::FUNC_ITEM)
 | |
|         {
 | |
|           gwip->fatalParseError = true;
 | |
|           gwip->parseErrorText =
 | |
|                 logging::IDBErrorInfo::instance()->errorMsg(logging::ERR_DATATYPE_NOT_SUPPORT, funcName);
 | |
|           return;
 | |
|         }
 | |
|         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<Item>* argumentList;
 | |
|       List<Item> 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<Item> li(*argumentList);
 | |
| 
 | |
|         while (Item* it = li++)
 | |
|         {
 | |
|           //@bug3495, @bug5865 error out non-supported OR with correlated subquery
 | |
|           if (isOr)
 | |
|           {
 | |
|             std::vector<Item_field*> 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<execplan::SimpleFilter*>(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<execplan::SimpleFilter*>(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<execplan::SimpleColumn*>(rc);
 | |
| 
 | |
|       if (sc)
 | |
|       {
 | |
|         boost::shared_ptr<execplan::SimpleColumn> scsp(sc->clone());
 | |
|         gwip->scsp = scsp;
 | |
| 
 | |
|         if (col->type() == Item::FIELD_ITEM)
 | |
|         {
 | |
|           const Item_ident* ident_field = dynamic_cast<const Item_ident*>(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<execplan::SimpleColumn*>(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<execplan::SimpleColumn*>(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*>(item), *gwip);
 | |
|         execplan::ReturnedColumn* rc =
 | |
|             buildReturnedColumn(const_cast<Item*>(item), *gwip, gwip->fatalParseError);
 | |
|         if (rc)
 | |
|           gwip->rcWorkStack.push(rc);
 | |
| 
 | |
|         break;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         cando = false;
 | |
|       }
 | |
| 
 | |
|       execplan::SimpleColumn* thisSC = dynamic_cast<execplan::SimpleColumn*>(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<execplan::SRCP> 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;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // CleanupGuard will automatically clean up if fatalParseError is set
 | |
|   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<Item_field*>& 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*>(item);
 | |
|       field_vec.push_back(ifp);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     case Item::SUM_FUNC_ITEM:
 | |
|     {
 | |
|       // hasAggColumn = true;
 | |
|       parseInfo |= AGG_BIT;
 | |
|       Item_sum* isp = static_cast<Item_sum*>(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_func*>(item);
 | |
| 
 | |
|       if (string(isp->func_name()) == "<in_optimizer>")
 | |
|       {
 | |
|         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_cond*>(item);
 | |
|       List_iterator_fast<Item> 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<Item_sum*>(*(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<Item_field*>(*(ref->ref));
 | |
|           field_vec.push_back(ifp);
 | |
|           //}
 | |
|           break;
 | |
|         }
 | |
|         else if ((*(ref->ref))->type() == Item::FUNC_ITEM)
 | |
|         {
 | |
|           Item_func* isp = static_cast<Item_func*>(*(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<Item_cache*>(*(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, *gwi);
 | |
|       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 << ": <NULL>" << 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<char*>(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<char*>(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*)"<NULL>";
 | |
|       }
 | |
| 
 | |
|       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<Item_cond*>(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;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace cal_impl_if
 |