QueryPlan *AttributeOrChildJoinQP::copy(XPath2MemoryManager *mm) const { if(!mm) mm = memMgr_; QueryPlan *result = new (mm) AttributeOrChildJoinQP(left_->copy(mm), right_->copy(mm), flags_, mm); result->setLocationInfo(this); return result; }
QueryPlan *DescendantOrSelfJoinQP::copy(XPath2MemoryManager *mm) const { if(!mm) mm = memMgr_; QueryPlan *result = new (mm) DescendantOrSelfJoinQP(left_->copy(mm), right_->copy(mm), flags_, mm); result->setLocationInfo(this); return result; }
QueryPlan *EmptyQP::copy(XPath2MemoryManager *mm) const { if(!mm) { mm = memMgr_; } QueryPlan *result = new (mm) EmptyQP(flags_, mm); result->setLocationInfo(this); return result; }
QueryPlan *IntersectQP::staticTyping(StaticContext *context, StaticTyper *styper) { _src.clear(); // Remove duplicate arguments vector<QueryPlan*> newArgs; Vector::iterator it = args_.begin(); if(it != args_.end()) { QueryPlan *arg = (*it)->staticTyping(context, styper); _src.copy(arg->getStaticAnalysis()); newArgs.push_back(arg); for(++it; it != args_.end(); ++it) { QueryPlan *arg = (*it)->staticTyping(context, styper); _src.add(arg->getStaticAnalysis()); _src.getStaticType().typeNodeIntersect(arg->getStaticAnalysis().getStaticType()); _src.setProperties(_src.getProperties() | arg->getStaticAnalysis().getProperties()); newArgs.push_back(arg); } args_.clear(); std::copy(newArgs.begin(), newArgs.end(), back_inserter(args_)); } _src.getStaticType().multiply(0, 1); return dissolve(); }
QueryPlan *AttributeOrChildJoinQP::staticTyping(StaticContext *context, StaticTyper *styper) { StructuralJoinQP::staticTyping(context, styper); XPath2MemoryManager *mm = context->getMemoryManager(); if(right_->getStaticAnalysis().getStaticType().isType(StaticType::ATTRIBUTE_TYPE)) { // Convert to a AttributeJoinQP QueryPlan *result = new (mm) AttributeJoinQP(left_, right_, flags_, mm); result->setLocationInfo(this); logTransformation((Manager&)GET_CONFIGURATION(context)->getManager(), "More specific join", this, result); return result->staticTyping(context, styper); } if(!right_->getStaticAnalysis().getStaticType().containsType(StaticType::ATTRIBUTE_TYPE)) { // Convert to a ChildJoinQP QueryPlan *result = new (mm) ChildJoinQP(left_, right_, flags_, mm); result->setLocationInfo(this); logTransformation((Manager&)GET_CONFIGURATION(context)->getManager(), "More specific join", this, result); return result->staticTyping(context, styper); } return this; }
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; }
DecisionPointQP::ListItem *DecisionPointQP::justInTimeOptimize(int contID, DynamicContext *context) { // **** IMPORTANT - This algorithm is very carefully arranged to avoid // **** deadlocks and race-conditions. Don't rearrange things unless you // **** know what you are doing! // Get the runtime configuration DbXmlConfiguration *conf = GET_CONFIGURATION(context); // Lookup the container ScopedContainer scont((Manager&)conf->getManager(), contID, /*mustExist*/true); // Just-in-time optimise the QueryPlan, using a temporary memory manager for thread safety XPath2MemoryManagerImpl tmpMM; QueryPlan *qp; { AutoMemoryManagerReset resetMM(context, &tmpMM); qp = arg_->copy(&tmpMM); try { AutoDecisionPointReset reset(conf, this); justInTimeOptimize(qp, scont.get(), context); } catch(XmlException &e) { if(e.getQueryLine() == 0) e.setLocationInfo(this); throw; } } // Hold the compile time mutex whilst altering the query plan. // This protects the compile time XPath2MemoryManager as well // as the query plan itself. // // (The mutex in the runtime configuration is the same as the // one from the compile time configuration.) MutexLock lock(conf->getMutex()); // Now we hold the lock, re-search qpList_ for the container, // in case someone beat us to creating it. DecisionPointQP::ListItem **li = &qpList_; while(*li != 0 && (*li)->container->getContainerID() < contID) { li = &(*li)->next; } if(*li == 0 || (*li)->container->getContainerID() != contID) { // Add the container to the compile time ReferenceMinder, in case it has been auto-opened if (contID > 0) compileTimeMinder_->addContainer(scont.getContainer()); // Create a new ListItem and copy the optimised QueryPlan using the // compile time memory manager - so that they can both become a // permanent part of the query's AST XPath2MemoryManager *compile_mm = compileTimeContext_->getMemoryManager(); DecisionPointQP::ListItem *newListItem = new (compile_mm) DecisionPointQP::ListItem(scont.get(), *li); newListItem->qp = qp->copy(compile_mm); newListItem->qp->staticTypingLite(compileTimeContext_); // Only add the new ListItem to the linked list once it is fully optimised // and ready to execute - otherwise another thread could come along and try // to use it. *li = newListItem; } else { // All our work was in vain! Someone beat us to creating a QueryPlan // for this container. Oh well, we'll just use the existing one then... } qp->release(); return *li; }
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; }