ParseTree* ExpressionParser::reduce(TreeNode* op, ParseTree* lhs, ParseTree* rhs) { ParseTree* root = new ParseTree(op); root->left(lhs); root->right(rhs); return root; }
/** * Build an expression tree with the tokens */ ParseTree* ExpressionParser::reduce(TreeNode* op, ParseTree* value) { char c = op->data().at(0); switch (c) { case 'M': case 'm': { ParseTree *root = new ParseTree(op); ParseTree *lhs = new ParseTree(new ConstantColumn("0", ConstantColumn::NUM)); root->left(lhs); root->right(value); return root; } case 'I': case 'i': delete op; return value; default: idbassert(0); } ostringstream oss; oss << "ExpressionParser::reduce(TreeNode*,ParseTree*): invalid input token: >" << op->data() << '<'; throw std::runtime_error(oss.str()); return 0; }
ParseTree* setDerivedFilter(ParseTree*& n, map<string, ParseTree*>& filterMap, erydbSelectExecutionPlan::SelectList& derivedTbList) { if (!(n->derivedTable().empty())) { // @todo replace virtual column of n to real column // all simple columns should belong to the same derived table erydbSelectExecutionPlan *csep = NULL; for (uint i = 0; i < derivedTbList.size(); i++) { erydbSelectExecutionPlan *plan = dynamic_cast<erydbSelectExecutionPlan*>(derivedTbList[i].get()); if (plan->derivedTbAlias() == n->derivedTable()) { csep = plan; break; } } // should never be null; if null then give up optimization. if (!csep) return n; // 2. push the filter to the derived table filter stack, or 'and' with // the filters in the stack map<string, ParseTree*>::iterator mapIter = filterMap.find(n->derivedTable()); if ( mapIter == filterMap.end()) { filterMap.insert(pair<string, ParseTree*>(n->derivedTable(), n)); } else { ParseTree* pt = new ParseTree(new LogicOperator("and")); pt->left(mapIter->second); pt->right(n); mapIter->second = pt; } int64_t val = 1; n = new ParseTree(new ConstantColumn(val)); } else { Operator *op = dynamic_cast<Operator*>(n->data()); if (op && (op->op() == OP_OR || op->op() == OP_XOR)) { return n; } else { ParseTree *lhs = n->left(); ParseTree *rhs = n->right(); if (lhs) n->left(setDerivedFilter(lhs, filterMap, derivedTbList)); if (rhs) n->right(setDerivedFilter(rhs, filterMap, derivedTbList)); } } return n; }
bool operator==(const ParseTree& t1, const ParseTree& t2) { if (t1.data() != NULL && t2.data() != NULL) { if (*t1.data() != t2.data()) return false; } else if (t1.data() != NULL || t2.data() != NULL) return false; if (t1.left() != NULL && t2.left() != NULL) { if (*t1.left() != *t2.left()) return false; } else if (t1.left() != NULL || t2.left() != NULL) return false; if (t1.right() != NULL && t2.right() != NULL) { if (*t1.right() != *t2.right()) return false; } else if (t1.right() != NULL || t2.right() != NULL) return false; return true; }
ParseTree* ObjectReader::createParseTree(messageqcpp::ByteStream& b) { CLASSID id = ZERO; ParseTree* ret; b >> (id_t&) id; if (id == NULL_CLASS) return NULL; if (id != PARSETREE) throw UnserializeException("Not a ParseTree"); ret = new ParseTree(); ret->left(createParseTree(b)); ret->right(createParseTree(b)); ret->data(createTreeNode(b)); return ret; }
void appendSimpleFilter ( ParseTree*& ptree, SimpleFilter* filter ) { if( ptree->data() == 0 ) { // degenerate case, this filter goes at this node ptree->data( filter ); } else if( ptree->right() == 0 && ptree->left() == 0 ) { // this will be the case when there is a single node in the tree // that contains a filter. Here we want to make the root node an // 'and' operator, push the existing down to the lhs and make a // new node for the new filter ParseTree* newLhs = new ParseTree( ptree->data() ); ParseTree* newRhs = new ParseTree( filter ); Operator* op = new Operator(); op->data("and"); ptree->data( op ); ptree->left( newLhs ); ptree->right( newRhs ); } else { // this will be the case once we have a tree with an 'and' at the // root node, a filter in the lhs, and an arbitrary height tree // with the same properties on the rhs. Because all operators // are guaranteed to be and for now we simply insert a new rhs // node and "push down" the existing tree Operator* op = new Operator(); op->data("and"); ParseTree* newRhs = new ParseTree( op ); newRhs->left( new ParseTree( filter ) ); newRhs->right( ptree->right() ); ptree->right( newRhs ); } }
void SubAdapterStep::addExpression(const JobStepVector& exps, JobInfo& jobInfo) { // maps key to the index in the RG map<uint32_t, uint32_t> keyToIndexMap; const vector<uint32_t>& keys = fRowGroupIn.getKeys(); for (size_t i = 0; i < keys.size(); i++) keyToIndexMap[keys[i]] = i; // combine the expression to one parse tree ParseTree* filter = NULL; for (JobStepVector::const_iterator it = exps.begin(); it != exps.end(); it++) { ExpressionStep* e = dynamic_cast<ExpressionStep*>(it->get()); idbassert(e); e->updateInputIndex(keyToIndexMap, jobInfo); if (filter != NULL) { ParseTree* left = filter; ParseTree* right = new ParseTree(); right->copyTree(*(e->expressionFilter())); filter = new ParseTree(new LogicOperator("and")); filter->left(left); filter->right(right); } else { filter = new ParseTree(); filter->copyTree(*(e->expressionFilter())); } } // add to the expression wrapper if (fExpression.get() == NULL) fExpression.reset(new funcexp::FuncExpWrapper()); fExpression->addFilter(boost::shared_ptr<execplan::ParseTree>(filter)); }
execplan::ParseTree* ScalarSub::transform_between() { //idbassert(fGwip.rcWorkStack.size() >= 3); if (fGwip.rcWorkStack.size() < 3) { fGwip.fatalParseError = true; fGwip.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SCALAR); return NULL; } ReturnedColumn* op3 = fGwip.rcWorkStack.top(); fGwip.rcWorkStack.pop(); ReturnedColumn* op2 = fGwip.rcWorkStack.top(); fGwip.rcWorkStack.pop(); ReturnedColumn* op1 = fGwip.rcWorkStack.top(); fGwip.rcWorkStack.pop(); fColumn.reset(op1); ParseTree* lhs = NULL; ParseTree* rhs = NULL; PredicateOperator* op_LE = new PredicateOperator("<="); PredicateOperator* op_GE = new PredicateOperator(">="); SubSelect* sub2 = dynamic_cast<SubSelect*>(op3); fSub = (Item_subselect*)(fFunc->arguments()[2]); if (sub2) { rhs = buildParseTree(op_LE); delete sub2; } else { SOP sop; sop.reset(op_LE); rhs = new ParseTree(new SimpleFilter(sop, fColumn.get(), op3)); } SubSelect* sub1 = dynamic_cast<SubSelect*>(op2); fSub = (Item_subselect*)(fFunc->arguments()[1]); if (sub1) { lhs = buildParseTree(op_GE); delete sub1; } else { SOP sop; sop.reset(op_GE); lhs = new ParseTree(new SimpleFilter(sop, fColumn.get(), op2)); } if (!rhs || !lhs) { fGwip.fatalParseError = true; fGwip.parseErrorText = "non-supported scalar subquery"; fGwip.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_NON_SUPPORT_SCALAR); return NULL; } ParseTree* pt = new ParseTree (new LogicOperator("and")); pt->left(lhs); pt->right(rhs); return pt; }
/** * Handle MySQL's plugin functions * This is mostly for handling the null related functions that MySQL adds to the execution plan */ void InSub::handleFunc(gp_walk_info* gwip, Item_func* func) { if (func->functype() == Item_func::TRIG_COND_FUNC || func->functype() == Item_func::COND_OR_FUNC) { // purpose: remove the isnull() function from the parsetree in ptWorkStack. // IDB handles the null semantics in the join operation // trigcond(or_cond) is the only form we recognize for now if (func->argument_count() > 2) { fGwip.fatalParseError = true; fGwip.parseErrorText = "Unsupported item in IN subquery"; return; } Item_cond* cond; if (func->functype() == Item_func::TRIG_COND_FUNC) { Item* item; if (func->arguments()[0]->type() == Item::REF_ITEM) item = (Item_ref*)(func->arguments()[0])->real_item(); else item = func->arguments()[0]; cond = (Item_cond*)(item); } else { cond = (Item_cond*)(func); } if (cond->functype() == Item_func::COND_OR_FUNC) { // (cache=item) case. do nothing. ignore trigcond()? if (cond->argument_list()->elements == 1) return; // (cache=item or isnull(item)) case. remove "or isnull()" if (cond->argument_list()->elements == 2) { // don't know how to deal with this. don't think it's a fatal error either. if (gwip->ptWorkStack.empty()) return; ParseTree* pt = gwip->ptWorkStack.top(); if (!pt->left() || !pt->right()) return; SimpleFilter* sf = dynamic_cast<SimpleFilter*>(pt->left()->data()); //assert (sf && sf->op()->op() == execplan::OP_ISNULL); if (!sf || sf->op()->op() != execplan::OP_ISNULL) return; delete sf; sf = dynamic_cast<SimpleFilter*>(pt->right()->data()); //idbassert(sf && sf->op()->op() == execplan::OP_EQ); if (!sf || sf->op()->op() != execplan::OP_EQ) return; // set NULLMATCH for both operand. It's really a setting for the join. // should only set NULLMATCH when the subtype is NOT_IN. for some IN subquery // with aggregation column, MySQL inefficiently convert to: // (cache=item or item is null) and item is not null, which is equivalent to // cache = item. Do not set NULLMATCH for this case. // Because we don't know IN or NOTIN yet, set candidate bit and switch to NULLMATCH // later in handleNot function. if (sf->lhs()->joinInfo() & JOIN_CORRELATED) sf->lhs()->joinInfo(sf->lhs()->joinInfo() | JOIN_NULLMATCH_CANDIDATE); if (sf->rhs()->joinInfo() & JOIN_CORRELATED) sf->rhs()->joinInfo(sf->rhs()->joinInfo() | JOIN_NULLMATCH_CANDIDATE); pt = pt->right(); gwip->ptWorkStack.pop(); gwip->ptWorkStack.push(pt); } } else if (cond->functype() == Item_func::EQ_FUNC) { // not in (select const ...) if (gwip->ptWorkStack.empty()) return; ParseTree* pt = gwip->ptWorkStack.top(); SimpleFilter* sf = dynamic_cast<SimpleFilter*>(pt->data()); if (!sf || sf->op()->op() != execplan::OP_EQ) return; if (sf->lhs()->joinInfo() & JOIN_CORRELATED) sf->lhs()->joinInfo(sf->lhs()->joinInfo() | JOIN_NULLMATCH_CANDIDATE); if (sf->rhs()->joinInfo() & JOIN_CORRELATED) sf->rhs()->joinInfo(sf->rhs()->joinInfo() | JOIN_NULLMATCH_CANDIDATE); } } }
void derivedTableOptimization(SCSEP& csep) { // @bug5634. replace the unused column with ConstantColumn from derived table column list, // ExeMgr will not project ConstantColumn. Only count for local derived column. // subquery may carry main query derived table list for column reference, those // derived tables are not checked for optimization in this scope. erydbSelectExecutionPlan::SelectList derivedTbList = csep->derivedTableList(); // @bug6156. Skip horizontal optimization for no table union. bool horizontalOptimization = true; for (uint i = 0; i < derivedTbList.size(); i++) { erydbSelectExecutionPlan *plan = dynamic_cast<erydbSelectExecutionPlan*>(derivedTbList[i].get()); erydbSelectExecutionPlan::ReturnedColumnList cols = plan->returnedCols(); vector<erydbSelectExecutionPlan::ReturnedColumnList> unionColVec; // only do vertical optimization for union all // @bug6134. Also skip the vertical optimization for select distinct // because all columns need to be projected to check the distinctness. bool verticalOptimization = false; if (plan->distinctUnionNum() == 0 && !plan->distinct()) { verticalOptimization = true; for (uint j = 0; j < plan->unionVec().size(); j++) { unionColVec.push_back( dynamic_cast<erydbSelectExecutionPlan*>(plan->unionVec()[j].get())->returnedCols()); } } if (plan->tableList().empty()) horizontalOptimization = false; for (uint j = 0; j < plan->unionVec().size(); j++) { if (dynamic_cast<erydbSelectExecutionPlan*>(plan->unionVec()[j].get())->tableList().empty()) { horizontalOptimization = false; break; } } if (verticalOptimization) { int64_t val = 1; for (uint i = 0; i < cols.size(); i++) { //if (cols[i]->derivedTable().empty()) if (cols[i]->refCount() == 0) { if (cols[i]->derivedRefCol()) cols[i]->derivedRefCol()->decRefCount(); cols[i].reset(new ConstantColumn(val)); for (uint j = 0; j < unionColVec.size(); j++) unionColVec[j][i].reset(new ConstantColumn(val)); } } // set back plan->returnedCols(cols); for (uint j = 0; j < unionColVec.size(); j++) dynamic_cast<erydbSelectExecutionPlan*>(plan->unionVec()[j].get())->returnedCols(unionColVec[j]); } } /* * @bug5635. Move filters that only belongs to a derived table to inside the derived table. * 1. parse tree walk to populate derivedTableFilterMap and set null candidate on the tree. * 2. remove the null filters * 3. and the filters of derivedTableFilterMap and append to the WHERE filter of the derived table * * Note: * 1. Subquery filters is ignored because derived table can not be in subquery * 2. While walking tree, whenever a single derive simplefilter is encountered, * this filter is pushed to the corresponding stack * 2. Whenever an OR operator is encountered, all the filter stack of * that OR involving derived table are emptied and null candidate of each * stacked filter needs to be reset (not null) */ ParseTree* pt = csep->filters(); map<string, ParseTree*> derivedTbFilterMap; if (horizontalOptimization && pt) { pt->walk(setDerivedTable); setDerivedFilter(pt, derivedTbFilterMap, derivedTbList); csep->filters(pt); } // AND the filters of individual stack to the derived table filter tree // @todo union filters. // @todo outer join complication map<string, ParseTree*>::iterator mapIt; for (uint i = 0; i < derivedTbList.size(); i++) { erydbSelectExecutionPlan *plan = dynamic_cast<erydbSelectExecutionPlan*>(derivedTbList[i].get()); erydbSelectExecutionPlan::ReturnedColumnList derivedColList = plan->returnedCols(); mapIt = derivedTbFilterMap.find(plan->derivedTbAlias()); if (mapIt != derivedTbFilterMap.end()) { // replace all derived column of this filter with real column from // derived table projection list. ParseTree *mainFilter = new ParseTree(); mainFilter->copyTree(*(mapIt->second)); replaceRefCol(mainFilter, derivedColList); ParseTree* derivedFilter = plan->filters(); if (derivedFilter) { LogicOperator *op = new LogicOperator("and"); ParseTree *filter = new ParseTree(op); filter->left(derivedFilter); filter->right(mainFilter); plan->filters(filter); } else { plan->filters(mainFilter); } // union filter handling for (uint j = 0; j < plan->unionVec().size(); j++) { erydbSelectExecutionPlan *unionPlan = dynamic_cast<erydbSelectExecutionPlan*>(plan->unionVec()[j].get()); erydbSelectExecutionPlan::ReturnedColumnList unionColList = unionPlan->returnedCols(); ParseTree* mainFilterForUnion = new ParseTree(); mainFilterForUnion->copyTree(*(mapIt->second)); replaceRefCol(mainFilterForUnion, unionColList); ParseTree *unionFilter = unionPlan->filters(); if (unionFilter) { LogicOperator *op = new LogicOperator("and"); ParseTree *filter = new ParseTree(op); filter->left(unionFilter); filter->right(mainFilterForUnion); unionPlan->filters(filter); } else { unionPlan->filters(mainFilterForUnion); } } } } // clean derivedTbFilterMap because all the filters are copied for( mapIt = derivedTbFilterMap.begin(); mapIt != derivedTbFilterMap.end(); ++mapIt ) delete (*mapIt).second; // recursively process the nested derived table for (uint i = 0; i < csep->subSelectList().size(); i++) { SCSEP subselect(boost::dynamic_pointer_cast<erydbSelectExecutionPlan>(csep->subSelectList()[i])); derivedTableOptimization(subselect); } }
ReturnedColumn* buildBoundExp(WF_Boundary& bound, SRCP& order, gp_walk_info& gwi) { if (!(gwi.thd->infinidb_vtable.cal_conn_info)) gwi.thd->infinidb_vtable.cal_conn_info = (void*)(new cal_connection_info()); cal_connection_info* ci = reinterpret_cast<cal_connection_info*>(gwi.thd->infinidb_vtable.cal_conn_info); bool addOp = true; ReturnedColumn* rc = NULL; if (bound.fFrame == execplan::WF_PRECEDING) { if (order->asc()) addOp = false; } else if (!order->asc()) // must be WF_FOLLOWING addOp = false; funcexp::FunctionParm funcParms; SPTP sptp; IntervalColumn* intervalCol = dynamic_cast<IntervalColumn*>(bound.fVal.get()); // @todo error out non constant. only support literal interval for now. if (!intervalCol && order->resultType().colDataType == CalpontSystemCatalog::DATE) { intervalCol = new IntervalColumn(bound.fVal, (int)IntervalColumn::INTERVAL_DAY); bound.fVal.reset(intervalCol); } if (intervalCol) { // date_add rc = new FunctionColumn(); string funcName = "date_add_interval"; // @bug6061 . YEAR, QUARTER, MONTH, WEEK, DAY type CalpontSystemCatalog::ColType ct; if (order->resultType().colDataType == CalpontSystemCatalog::DATE && intervalCol->intervalType() <= IntervalColumn::INTERVAL_DAY) { ct.colDataType = CalpontSystemCatalog::DATE; ct.colWidth = 4; } else { ct.colDataType = CalpontSystemCatalog::DATETIME; ct.colWidth = 8; } // put interval val column to bound (dynamic_cast<FunctionColumn*>(rc))->functionName(funcName); sptp.reset(new ParseTree(order->clone())); funcParms.push_back(sptp); sptp.reset(new ParseTree(intervalCol->val()->clone())); funcParms.push_back(sptp); funcParms.push_back(getIntervalType(intervalCol->intervalType())); SRCP srcp(intervalCol->val()); bound.fVal = srcp; if (addOp) { sptp.reset(new ParseTree(new ConstantColumn("ADD"))); funcParms.push_back(sptp); } else { sptp.reset(new ParseTree(new ConstantColumn("SUB"))); funcParms.push_back(sptp); } (dynamic_cast<FunctionColumn*>(rc))->functionParms(funcParms); rc->resultType(ct); // @bug6061. Use result type as operation type for WF bound expression rc->operationType(ct); rc->expressionId(ci->expressionId++); return rc; } // arithmetic rc = new ArithmeticColumn(); ArithmeticOperator* aop; if (addOp) aop = new ArithmeticOperator("+"); else aop = new ArithmeticOperator("-"); ParseTree *pt = new ParseTree(aop); ParseTree *lhs = 0, *rhs = 0; lhs = new ParseTree(order->clone()); rhs = new ParseTree(bound.fVal->clone()); pt->left(lhs); pt->right(rhs); aop->resultType(order->resultType()); aop->operationType(aop->resultType()); (dynamic_cast<ArithmeticColumn*>(rc))->expression(pt); rc->resultType(aop->resultType()); rc->operationType(aop->operationType()); rc->expressionId(ci->expressionId++); return rc; }