use of org.apache.derby.iapi.sql.compile.OptimizablePredicateList in project derby by apache.
the class PredicateList method transferAllPredicates.
/**
* @see OptimizablePredicateList#transferAllPredicates
*
* @exception StandardException Thrown on error
*/
public void transferAllPredicates(OptimizablePredicateList otherList) throws StandardException {
PredicateList theOtherList = (PredicateList) otherList;
for (Predicate predicate : this) {
/*
** Clear all of the scan flags since they may be different
** when the new list is re-classified
*/
predicate.clearScanFlags();
// Add the predicate to the other list
theOtherList.addPredicate(predicate);
}
// Remove all of the predicates from this list
removeAllElements();
/*
** This list is now empty, so there are no start predicates,
** stop predicates, or qualifiers.
*/
numberOfStartPredicates = 0;
numberOfStopPredicates = 0;
numberOfQualifiers = 0;
}
use of org.apache.derby.iapi.sql.compile.OptimizablePredicateList in project derby by apache.
the class PredicateList method selectivity.
/**
* @see OptimizablePredicateList#selectivity
*/
public double selectivity(Optimizable optTable) throws StandardException {
TableDescriptor td = optTable.getTableDescriptor();
ConglomerateDescriptor[] conglomerates = td.getConglomerateDescriptors();
int numPredicates = size();
int numConglomerates = conglomerates.length;
if (numConglomerates == 1)
// one conglomerate; must be heap.
return -1.0d;
if (numPredicates == 0)
// no predicates why bother?
return -1.0d;
boolean nothingYet = true;
/* before we start, lets select non-redundant prediates into a working
* list; we'll work with the workingPredicates list from now on in this
* routine.
*/
PredicateList workingPredicates = new PredicateList(getContextManager());
for (int i = 0; i < numPredicates; i++) {
if (isRedundantPredicate(i))
continue;
/* to workingPredicates only add useful predicates... */
workingPredicates.addOptPredicate(elementAt(i));
}
int numWorkingPredicates = workingPredicates.size();
/*--------------------------------------------------------------------
* In the first phase, the routine initializes an array of
* predicateWrapperLists-- one list for each conglomerate that has
* statistics.
*
* predsForConglomerates is an array of pwList. For each conglomerate we
* keep a pwList of predicates that have an equals predicate on a column
* in the conglomerate.
*
* As an example consider a table T, with indices on
* T(c1)-->conglom_one, T(c2,c1)-->conglom_two.
*
* if we have the following predicates:
* T.c1=T1.x (p1) and T.c1=T2.y (p2) and T.c2=T1.z (p3), then we'll have the
* after the first loop is done, we'll have the following setup.
*
* conglom_one: pwList [p1,p2]
* conglom_two: pwList [p1,p2,p3].
*
* Note that although p1,p2 appear on both conglomerates, the
* indexPosition of p1 and p2 on the first list is 0 (first index
* position) while the index position of p1,p2 on the second list is 1
* (second index position).
*
* PredicateWrapper and PredicateWrapperLists are inner classes used
* only in this file.
* -------------------------------------------------------------------- */
PredicateWrapperList[] predsForConglomerates = new PredicateWrapperList[numConglomerates];
for (int i = 0; i < numConglomerates; i++) {
ConglomerateDescriptor cd = conglomerates[i];
if (!cd.isIndex())
continue;
if (!td.statisticsExist(cd))
continue;
int[] baseColumnList = cd.getIndexDescriptor().baseColumnPositions();
for (int j = 0; j < numWorkingPredicates; j++) {
Predicate pred = workingPredicates.elementAt(j);
int ip = pred.hasEqualOnColumnList(baseColumnList, optTable);
if (ip < 0)
// look at the next predicate.
continue;
nothingYet = false;
if (predsForConglomerates[i] == null) {
predsForConglomerates[i] = new PredicateWrapperList(numWorkingPredicates);
}
PredicateWrapper newpw = new PredicateWrapper(ip, pred, j);
predsForConglomerates[i].insert(newpw);
}
// for (j = 0;
}
if (nothingYet) {
return -1.0;
}
/*------------------------------------------------------------------
* In the second phase we,
* walk the predsForConglomerateList again-- if we find
* a break in the indexPositions we remove the predicates
* after the gap. To clarify, if we have equal predicates on the first
* and the third index positions, we can throw away the predicate on
* the 3rd index position-- it doesn't do us any good.
*-------------------------------------------------------------------*/
int maxOverlap = -1;
for (int i = 0; i < numConglomerates; i++) {
if (predsForConglomerates[i] == null)
continue;
predsForConglomerates[i].retainLeadingContiguous();
}
// for (i = 0; i < ...)
calculateWeight(predsForConglomerates, numWorkingPredicates);
/*-------------------------------------------------------------------
* In the third phase we loop through predsForConglomerates choosing the
* best fit (chooseLongestMatch) of predicates. we use the statistics
* for the set of predicates returned by chooseLongestMatch and then
* loop until we can't find any more statistics or we have exhausted all
* the predicates for which we are trying to find statistics.
*--------------------------------------------------------------------*/
double selectivity = 1.0;
ArrayList<Predicate> maxPreds = new ArrayList<Predicate>();
while (true) {
maxPreds.clear();
int conglomIndex = chooseLongestMatch(predsForConglomerates, maxPreds, numWorkingPredicates);
if (conglomIndex == -1)
// no more stats available.
break;
selectivity *= td.selectivityForConglomerate(conglomerates[conglomIndex], maxPreds.size());
for (int i = 0; i < maxPreds.size(); i++) {
/* remove the predicates that we've calculated the selectivity
* of, from workingPredicates.
*/
Predicate p = maxPreds.get(i);
workingPredicates.removeOptPredicate(p);
}
if (workingPredicates.size() == 0)
break;
}
if (workingPredicates.size() != 0) {
selectivity *= workingPredicates.selectivityNoStatistics(optTable);
}
return selectivity;
}
use of org.apache.derby.iapi.sql.compile.OptimizablePredicateList in project derby by apache.
the class FromBaseTable method isOneRowResultSet.
/**
* Is this a one-row result set with the given conglomerate descriptor?
*/
private boolean isOneRowResultSet(ConglomerateDescriptor cd, OptimizablePredicateList predList) throws StandardException {
if (predList == null) {
return false;
}
if (SanityManager.DEBUG) {
if (!(predList instanceof PredicateList)) {
SanityManager.THROWASSERT("predList should be a PredicateList, but is a " + predList.getClass().getName());
}
}
PredicateList restrictList = (PredicateList) predList;
if (!cd.isIndex()) {
return false;
}
IndexRowGenerator irg = cd.getIndexDescriptor();
// is this a unique index
if (!irg.isUnique()) {
return false;
}
int[] baseColumnPositions = irg.baseColumnPositions();
// Do we have an exact match on the full key
for (int index = 0; index < baseColumnPositions.length; index++) {
// get the column number at this position
int curCol = baseColumnPositions[index];
/* Is there a pushable equality predicate on this key column?
* (IS NULL is also acceptable)
*/
if (!restrictList.hasOptimizableEqualityPredicate(this, curCol, true)) {
return false;
}
}
return true;
}
use of org.apache.derby.iapi.sql.compile.OptimizablePredicateList in project derby by apache.
the class OptimizerImpl method getNextDecoratedPermutation.
/**
* @see Optimizer#getNextDecoratedPermutation
*
* @exception StandardException Thrown on error
*/
public boolean getNextDecoratedPermutation() throws StandardException {
boolean retval;
Optimizable curOpt = optimizableList.getOptimizable(proposedJoinOrder[joinPosition]);
double originalRowCount = 0.0;
while (true) {
/* Returns true until all access paths are exhausted */
retval = curOpt.nextAccessPath(this, (OptimizablePredicateList) null, currentRowOrdering);
// if the user didn't specify an explicit plan, we're ok
if (overridingPlan == null) {
break;
}
if (!retval) {
break;
}
// the join order, then we move on to the next position
if (currentPlan != null) {
if (currentPlan.countLeafNodes() == (joinPosition + 1)) {
retval = false;
break;
}
}
// at this point, figure out if the plan so far is a prefix of the desired plan
OptimizerPlan candidate = OptimizerPlan.makeRowSource(getTupleDescriptor(curOpt), dDictionary);
if (candidate == null) {
retval = false;
break;
}
if (currentPlan != null) {
candidate = new OptimizerPlan.Join(curOpt.getCurrentAccessPath().getJoinStrategy(), currentPlan, candidate);
}
if (candidate.isLeftPrefixOf(overridingPlan)) {
currentPlan = candidate;
break;
}
// well, that decoration didn't match up with the user-specified plan.
// try again
}
// checked was the only one possible for this round.
if ((curOpt.getBestAccessPath().getCostEstimate() != null) && (curOpt.getCurrentAccessPath().getCostEstimate() != null)) {
// by "current" access path) was not the best.
if (curOpt.getBestAccessPath().getCostEstimate().compare(curOpt.getCurrentAccessPath().getCostEstimate()) != 0) {
curOpt.updateBestPlanMap(FromTable.LOAD_PLAN, curOpt);
} else if (curOpt.getBestAccessPath().getCostEstimate().rowCount() < curOpt.getCurrentAccessPath().getCostEstimate().rowCount()) {
// If currentCost and bestCost have the same cost estimate
// but currentCost has been rejected because of memory, we
// still need to revert the plans. In this case the row
// count for currentCost will be greater than the row count
// for bestCost, so that's what we just checked.
curOpt.updateBestPlanMap(FromTable.LOAD_PLAN, curOpt);
}
}
/* If we needed to revert plans for curOpt, we just did it above.
* So we no longer need to keep the previous best plan--and in fact,
* keeping it can lead to extreme memory usage for very large
* queries. So delete the stored plan for curOpt. DERBY-1315.
*/
curOpt.updateBestPlanMap(FromTable.REMOVE_PLAN, curOpt);
/*
** When all the access paths have been looked at, we know what the
** cheapest one is, so remember it. Only do this if a cost estimate
** has been set for the best access path - one will not have been
** set if no feasible plan has been found.
*/
CostEstimate ce = curOpt.getBestAccessPath().getCostEstimate();
if ((!retval) && (ce != null)) {
/*
** Add the cost of the current optimizable to the total cost.
** The total cost is the sum of all the costs, but the total
** number of rows is the number of rows returned by the innermost
** optimizable.
*/
currentCost.setCost(currentCost.getEstimatedCost() + ce.getEstimatedCost(), ce.rowCount(), ce.singleScanRowCount());
if (curOpt.considerSortAvoidancePath() && requiredRowOrdering != null) {
/* Add the cost for the sort avoidance path, if there is one */
ce = curOpt.getBestSortAvoidancePath().getCostEstimate();
currentSortAvoidanceCost.setCost(currentSortAvoidanceCost.getEstimatedCost() + ce.getEstimatedCost(), ce.rowCount(), ce.singleScanRowCount());
}
if (tracingIsOn()) {
tracer().traceCostWithoutSortAvoidance(currentCost);
if (curOpt.considerSortAvoidancePath()) {
tracer().traceCostWithSortAvoidance(currentSortAvoidanceCost);
}
}
/* Do we have a complete join order? */
if (joinPosition == (numOptimizables - 1)) {
if (tracingIsOn()) {
tracer().traceCompleteJoinOrder();
}
/* Add cost of sorting to non-sort-avoidance cost */
if (requiredRowOrdering != null) {
boolean gotSortCost = false;
/* Only get the sort cost once */
if (sortCost == null) {
sortCost = newCostEstimate();
} else /* requiredRowOrdering records if the bestCost so far is
* sort-needed or not, as done in rememberBestCost. If
* the bestCost so far is sort-needed, and assume
* currentCost is also sort-needed, we want this comparison
* to be as accurate as possible. Different plans may
* produce different estimated row count (eg., heap scan
* vs. index scan during a join), sometimes the difference
* could be very big. However the actual row count should
* be only one value. So when comparing these two plans,
* we want them to have the same sort cost. We want to
* take the smaller row count, because a better estimation
* (eg. through index) would yield a smaller number. We
* adjust the bestCost here if it had a bigger rowCount
* estimate. The performance improvement of doing this
* sometimes is quite dramatic, eg. from 17 sec to 0.5 sec,
* see beetle 4353.
*/
if (requiredRowOrdering.getSortNeeded()) {
if (bestCost.rowCount() > currentCost.rowCount()) {
// adjust bestCost
requiredRowOrdering.estimateCost(bestCost.rowCount(), bestRowOrdering, sortCost);
double oldSortCost = sortCost.getEstimatedCost();
requiredRowOrdering.estimateCost(currentCost.rowCount(), bestRowOrdering, sortCost);
gotSortCost = true;
bestCost.setCost(bestCost.getEstimatedCost() - oldSortCost + sortCost.getEstimatedCost(), sortCost.rowCount(), currentCost.singleScanRowCount());
} else if (bestCost.rowCount() < currentCost.rowCount()) {
// adjust currentCost's rowCount
currentCost.setCost(currentCost.getEstimatedCost(), bestCost.rowCount(), currentCost.singleScanRowCount());
}
}
/* This does not figure out if sorting is necessary, just
* an asumption that sort is needed; if the assumption is
* wrong, we'll look at sort-avoidance cost as well, later
*/
if (!gotSortCost) {
requiredRowOrdering.estimateCost(currentCost.rowCount(), bestRowOrdering, sortCost);
}
originalRowCount = currentCost.rowCount();
currentCost.setCost(currentCost.getEstimatedCost() + sortCost.getEstimatedCost(), sortCost.rowCount(), currentCost.singleScanRowCount());
if (tracingIsOn()) {
tracer().traceSortCost(sortCost, currentCost);
}
}
/*
** Is the cost of this join order lower than the best one we've
** found so far?
**
** NOTE: If the user has specified a join order, it will be the
** only join order the optimizer considers, so it is OK to use
** costing to decide that it is the "best" join order.
**
** For very deeply nested queries, it's possible that the optimizer
** will return an estimated cost of Double.INFINITY, which is
** greater than our uninitialized cost of Double.MAX_VALUE and
** thus the "compare" check below will return false. So we have
** to check to see if bestCost is uninitialized and, if so, we
** save currentCost regardless of what value it is--because we
** haven't found anything better yet.
**
** That said, it's also possible for bestCost to be infinity
** AND for current cost to be infinity, as well. In that case
** we can't really tell much by comparing the two, so for lack
** of better alternative we look at the row counts. See
** CostEstimateImpl.compare() for more.
*/
if ((!foundABestPlan) || (currentCost.compare(bestCost) < 0) || bestCost.isUninitialized()) {
rememberBestCost(currentCost, Optimizer.NORMAL_PLAN);
// Since we just remembered all of the best plans,
// no need to reload them when pulling Optimizables
// from this join order.
reloadBestPlan = false;
} else
reloadBestPlan = true;
/* Subtract cost of sorting from non-sort-avoidance cost */
if (requiredRowOrdering != null) {
/*
** The cost could go negative due to loss of precision.
*/
double newCost = currentCost.getEstimatedCost() - sortCost.getEstimatedCost();
if (newCost < 0.0)
newCost = 0.0;
currentCost.setCost(newCost, originalRowCount, currentCost.singleScanRowCount());
}
/*
** This may be the best sort-avoidance plan if there is a
** required row ordering, and we are to consider a sort
** avoidance path on the last Optimizable in the join order.
*/
if (requiredRowOrdering != null && curOpt.considerSortAvoidancePath()) {
if (requiredRowOrdering.sortRequired(bestRowOrdering, optimizableList, proposedJoinOrder) == RequiredRowOrdering.NOTHING_REQUIRED) {
if (tracingIsOn()) {
tracer().traceCurrentPlanAvoidsSort(bestCost, currentSortAvoidanceCost);
}
if ((currentSortAvoidanceCost.compare(bestCost) <= 0) || bestCost.isUninitialized()) {
rememberBestCost(currentSortAvoidanceCost, Optimizer.SORT_AVOIDANCE_PLAN);
}
}
}
}
}
return retval;
}
Aggregations