QueryPlan *AttributeOrChildJoinQP::optimize(OptimizationContext &opt) { QueryPlan *result = StructuralJoinQP::optimize(opt); if(result != this) return result; XPath2MemoryManager *mm = opt.getMemoryManager(); ImpliedSchemaNode::Type type = findType(right_); if(type == ImpliedSchemaNode::ATTRIBUTE) { // Convert to a AttributeJoinQP QueryPlan *result = new (mm) AttributeJoinQP(left_, right_, flags_, mm); result->setLocationInfo(this); logTransformation(opt.getLog(), "More specific join", this, result); return result->optimize(opt); } if(type != -1) { // Convert to a ChildJoinQP QueryPlan *result = new (mm) ChildJoinQP(left_, right_, flags_, mm); result->setLocationInfo(this); logTransformation(opt.getLog(), "More specific join", this, result); return result->optimize(opt); } return this; }
QueryPlan *IntersectQP::optimize(OptimizationContext &opt) { // Optimize the arguments vector<QueryPlan*> newArgs; Vector::iterator it; for(it = args_.begin(); it != args_.end(); ++it) { QueryPlan *arg = (*it)->optimize(opt); if(arg->getType() == type_) { // Merge IntersectQP arguments into this object const Vector &args = ((OperationQP*)arg)->getArgs(); Vector::const_iterator aend = args.end(); for(Vector::const_iterator ai = args.begin(); ai != aend; ++ai) newArgs.push_back(*ai); } else { newArgs.push_back(arg); } } args_.clear(); std::copy(newArgs.begin(), newArgs.end(), back_inserter(args_)); removeSupersets(opt); // Return if we only have one argument if(args_.size() == 1) return args_[0]; // Pull forward filters for(it = args_.begin(); it != args_.end(); ++it) { switch((*it)->getType()) { case VALUE_FILTER: case PREDICATE_FILTER: case NODE_PREDICATE_FILTER: case NEGATIVE_NODE_PREDICATE_FILTER: // We can't skip NUMERIC_PREDICATE_FILTER, because changing it's input will change // the cardinality of it's input, and cause it to return the wrong results. // case NUMERIC_PREDICATE_FILTER: // case DOC_EXISTS: case LEVEL_FILTER: { string before = logBefore(this); FilterQP *filter = (FilterQP*)*it; *it = filter->getArg(); filter->setArg(this); logTransformation(opt.getLog(), "Filter pulled forward", before, filter); return filter->optimize(opt); } default: break; } } // Identify potential pairs of ValueQP for a RangeQP newArgs.clear(); for(it = args_.begin(); it != args_.end(); ++it) { if(isLessThanOrGreaterThan(*it)) { for(Vector::iterator it2 = it + 1; it2 != args_.end(); ++it2) { if(isLessThanOrGreaterThan(*it2)) { QueryPlan *range = createRange((ValueQP*)*it, (ValueQP*)*it2); if(range != 0) { logTransformation(opt.getLog(), "Merged into range", logIntersectBefore(*it, *it2), range); newArgs.push_back(range); } } } } newArgs.push_back((*it)->optimize(opt)); } args_.clear(); std::copy(newArgs.begin(), newArgs.end(), back_inserter(args_)); // Return if we only have one argument if(args_.size() == 1) return args_[0]; // Try to pull forward document indexes, so they will combine if(opt.getPhase() < OptimizationContext::ALTERNATIVES) { string before = logBefore(this); QueryPlan *result = PullForwardDocumentJoin().run(this); if(result != 0) { logTransformation(opt.getLog(), "Pull forward document join", before, result); return result->optimize(opt); } } if(opt.getPhase() < OptimizationContext::REMOVE_REDUNDENTS) return this; // TBD remove the need for QueryExecutionContext here - jpcs QueryExecutionContext qec(GET_CONFIGURATION(opt.getContext())->getQueryContext(), /*debugging*/false); qec.setContainerBase(opt.getContainerBase()); qec.setDynamicContext(opt.getContext()); // Sort the arguments based on how many keys we think they'll return sort(args_.begin(), args_.end(), keys_compare_less(opt.getOperationContext(), qec)); // Remove document index joins if they are unlikely to be useful. it = args_.begin(); QueryPlan *leftMost = *it; Cost lCost = leftMost->cost(opt.getOperationContext(), qec); newArgs.clear(); newArgs.push_back(*it); for(++it; it != args_.end(); ++it) { if(StructuralJoinQP::isDocumentIndex(*it, /*toBeRemoved*/true) && StructuralJoinQP::isSuitableForDocumentIndexComparison(leftMost)) { Cost itCost = (*it)->cost(opt.getOperationContext(), qec); // TBD Calculate these constants? - jpcs static const double KEY_RATIO_THRESHOLD = 2.0; static const double PAGES_PER_KEY_FACTOR = 2.0; if((itCost.keys / lCost.keys) > KEY_RATIO_THRESHOLD || (itCost.totalPages() / itCost.keys) > (lCost.totalPages() * PAGES_PER_KEY_FACTOR / lCost.keys)) { string before = logIntersectBefore(leftMost, *it); logTransformation(opt.getLog(), "Remove document join", before, leftMost); leftMost->logCost(qec, lCost, 0); (*it)->logCost(qec, itCost, 0); continue; } } newArgs.push_back(*it); } args_.clear(); std::copy(newArgs.begin(), newArgs.end(), back_inserter(args_)); // Return if we only have one argument if(args_.size() == 1) return args_[0]; return this; }
QueryPlan *DescendantOrSelfJoinQP::optimize(OptimizationContext &opt) { XPath2MemoryManager *mm = opt.getMemoryManager(); QueryPlan *qp = StructuralJoinQP::optimize(opt); if(qp != this) return qp; if(findType(left_) == ImpliedSchemaNode::METADATA) { switch(right_->getType()) { case DESCENDANT_OR_SELF: { DescendantOrSelfJoinQP *rsj = (DescendantOrSelfJoinQP*)right_; if(findType(rsj->left_) == ImpliedSchemaNode::METADATA) { string before = logBefore(this); left_ = new (mm) IntersectQP(left_, rsj->left_, 0, mm); left_->setLocationInfo(rsj); right_ = rsj->right_; flags_ |= rsj->flags_; logTransformation(opt.getLog(), "Combine document join", before, this); return optimize(opt); } break; } default: { if(findType(right_) == ImpliedSchemaNode::METADATA) { string before = logBefore(this); QueryPlan *result = new (mm) IntersectQP(left_, right_, flags_, mm); result->setLocationInfo(this); logTransformation(opt.getLog(), "Combine document join", this, result); return result->optimize(opt); } break; } } } if(opt.getPhase() < OptimizationContext::REMOVE_REDUNDENTS) return this; if(findType(left_) == ImpliedSchemaNode::METADATA) { switch(right_->getType()) { case STEP: { string before = logBefore(this); StepQP *step = (StepQP*)right_; right_ = step->getArg(); step->setArg(this); logTransformation(opt.getLog(), "Push back document join", before, step); return step->optimize(opt); } case DESCENDANT: case DESCENDANT_OR_SELF: case ATTRIBUTE: case CHILD: case ATTRIBUTE_OR_CHILD: { string before = logBefore(this); StructuralJoinQP *sj = (StructuralJoinQP*)right_; right_ = sj->getLeftArg(); sj->setLeftArg(this); logTransformation(opt.getLog(), "Push back document join", before, sj); return sj->optimize(opt); } case ANCESTOR: case ANCESTOR_OR_SELF: case PARENT: case PARENT_OF_ATTRIBUTE: case PARENT_OF_CHILD: { string before = logBefore(this); StructuralJoinQP *sj = (StructuralJoinQP*)right_; right_ = sj->getRightArg(); sj->setRightArg(this); logTransformation(opt.getLog(), "Push back document join", before, sj); return sj->optimize(opt); } case EXCEPT: { string before = logBefore(this); // Add to both arguments of ExceptQP ExceptQP *ex = (ExceptQP*)right_; right_ = ex->getLeftArg(); ex->setLeftArg(this); QueryPlan *copy = new (mm) DescendantOrSelfJoinQP(left_->copy(mm), ex->getRightArg(), flags_, mm); copy->setLocationInfo(this); ex->setRightArg(copy); logTransformation(opt.getLog(), "Push back document join", before, ex); return ex->optimize(opt); } default: break; } } // TBD remove the need for QueryExecutionContext here - jpcs QueryExecutionContext qec(GET_CONFIGURATION(opt.getContext())->getQueryContext(), /*debugging*/false); qec.setContainerBase(opt.getContainerBase()); qec.setDynamicContext(opt.getContext()); // Remove this document index join if it's unlikely to be useful if(isDocumentIndex(left_, /*toBeRemoved*/true) && isSuitableForDocumentIndexComparison(right_)) { Cost rCost = right_->cost(opt.getOperationContext(), qec); Cost lCost = left_->cost(opt.getOperationContext(), qec); // TBD Calculate these constants? - jpcs static const double KEY_RATIO_THRESHOLD = 2.0; static const double KEYS_PER_PAGE_THRESHOLD = 2.0; if((lCost.keys / rCost.keys) > KEY_RATIO_THRESHOLD || (lCost.keys / lCost.totalPages()) > KEYS_PER_PAGE_THRESHOLD) { logTransformation(opt.getLog(), "Remove document join", this, right_); right_->logCost(qec, rCost, 0); left_->logCost(qec, lCost, 0); return right_; } } return this; }