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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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;
}