You've already forked mariadb-columnstore-engine
mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-07-29 08:21:15 +03:00
1636 lines
49 KiB
C++
1636 lines
49 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"
|
|
|
|
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 <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);
|
|
|
|
// 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<execplan::SimpleColumn> 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*>(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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
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
|
|
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)
|
|
{
|
|
set<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)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
} |