| /* |
| |
| Derby - Class org.apache.derby.impl.sql.compile.CostEstimateImpl |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to you under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| package org.apache.derby.impl.sql.compile; |
| |
| import org.apache.derby.shared.common.sanity.SanityManager; |
| import org.apache.derby.iapi.sql.compile.CostEstimate; |
| import org.apache.derby.iapi.store.access.StoreCostResult; |
| |
| class CostEstimateImpl implements CostEstimate { |
| double cost; |
| double rowCount; |
| double singleScanRowCount; |
| |
| CostEstimateImpl() { |
| } |
| |
| CostEstimateImpl(double theCost, |
| double theRowCount, |
| double theSingleScanRowCount) { |
| if (SanityManager.DEBUG) |
| { |
| if (theCost < 0.0 || |
| theRowCount < 0.0 || |
| theSingleScanRowCount < 0.0) |
| { |
| SanityManager.THROWASSERT( |
| "All parameters expected to be < 0.0, " + |
| "\n\ttheCost = " + theCost + |
| "\n\ttheRowCount = " + theRowCount + |
| "\n\ttheSingleScanRowCount = " + theSingleScanRowCount |
| ); |
| } |
| } |
| this.cost = theCost; |
| this.rowCount = theRowCount; |
| this.singleScanRowCount = theSingleScanRowCount; |
| } |
| |
| /** @see CostEstimate#setCost */ |
| public void setCost(double cost, double rowCount, |
| double singleScanRowCount) { |
| if (SanityManager.DEBUG) |
| { |
| if (cost < 0.0 || |
| rowCount < 0.0 || |
| singleScanRowCount < 0.0) |
| { |
| SanityManager.THROWASSERT( |
| "All parameters expected to be < 0.0, " + |
| "\n\tcost = " + cost + |
| "\n\trowCount = " + rowCount + |
| "\n\tsingleScanRowCount = " + singleScanRowCount |
| ); |
| } |
| } |
| this.cost = cost; |
| this.rowCount = rowCount; |
| this.singleScanRowCount = singleScanRowCount; |
| } |
| |
| /** @see CostEstimate#setCost */ |
| public void setCost(CostEstimate other) { |
| cost = other.getEstimatedCost(); |
| rowCount = other.rowCount(); |
| singleScanRowCount = other.singleScanRowCount(); |
| } |
| |
| /** @see CostEstimate#setSingleScanRowCount */ |
| public void setSingleScanRowCount(double singleScanRowCount) |
| { |
| if (SanityManager.DEBUG) |
| { |
| if (singleScanRowCount < 0.0) |
| { |
| SanityManager.THROWASSERT( |
| "All parameters expected to be < 0.0, " + |
| "\n\tsingleScanRowCount = " + singleScanRowCount |
| ); |
| } |
| } |
| this.singleScanRowCount = singleScanRowCount; |
| } |
| |
| /** @see CostEstimate#compare */ |
| public double compare(CostEstimate other) { |
| if (SanityManager.DEBUG) { |
| if (other == null) { |
| SanityManager.THROWASSERT("Comparing with null CostEstimate"); |
| } |
| |
| if ( ! (other instanceof CostEstimateImpl)) { |
| SanityManager.THROWASSERT(other.getClass().getName()); |
| } |
| } |
| |
| /* Note: if both CostEstimates are infinity, an attempt to |
| * substract them will result in NaN, which tells us nothing |
| * and thus makes it impossible to do a comparison. So in |
| * that case we fallback and check the row counts as a secondary |
| * point of comparison, and the singleScanRowCounts as a |
| * third comparison. If all three values are infinity |
| * for both CostEstimates then we just consider the two |
| * costs to equal (equally as bad?) and so return 0.0d (instead |
| * NaN). RESOLVE: Ideally the optimizer could be updated |
| * to give more reasonable estimates than infinity, but |
| * until that happens we're forced to deal with such |
| * comparisons. Note that we're most likely to end up with |
| * infinite cost estimates in situations where we have deeply |
| * nested subqueries and/or FROM lists with a large number of |
| * FromTables (such as 10 or more). The reason is that each |
| * FromTable's cost estimate is (potentially) multiplied by |
| * the row counts of all preceding FromTables, so if the |
| * row counts for the preceding FromTables are large, we |
| * can eventually end up going beyond Double.MAX_VALUE, |
| * which then gives us infinity. |
| */ |
| |
| // If at least one of costs is _not_ infinity, then just do |
| // a normal compare (the other side is less). |
| if ((this.cost != Double.POSITIVE_INFINITY) || |
| (other.getEstimatedCost() != Double.POSITIVE_INFINITY)) |
| { |
| return this.cost - ((CostEstimateImpl) other).cost; |
| } |
| |
| // If both costs are infinity, then compare row counts. |
| if ((this.rowCount != Double.POSITIVE_INFINITY) || |
| (other.rowCount() != Double.POSITIVE_INFINITY)) |
| { |
| return this.rowCount - other.rowCount(); |
| } |
| |
| // If both row counts are infinity, try singleScan counts. |
| if ((this.singleScanRowCount != Double.POSITIVE_INFINITY) || |
| (other.singleScanRowCount() != Double.POSITIVE_INFINITY)) |
| { |
| return this.singleScanRowCount - other.singleScanRowCount(); |
| } |
| |
| // If we get here, all three parts of both cost estimates are |
| // Infinity; for lack of better choice, just say they're "equal". |
| return 0.0d; |
| } |
| |
| /** @see CostEstimate#add */ |
| public CostEstimate add(CostEstimate other, CostEstimate retval) { |
| if (SanityManager.DEBUG) { |
| SanityManager.ASSERT(other instanceof CostEstimateImpl); |
| SanityManager.ASSERT(retval == null || |
| retval instanceof CostEstimateImpl); |
| } |
| |
| CostEstimateImpl addend = (CostEstimateImpl) other; |
| |
| double sumCost = this.cost + addend.cost; |
| double sumRowCount = this.rowCount + addend.rowCount; |
| if (SanityManager.DEBUG) |
| { |
| if (sumCost < 0.0 || |
| sumRowCount < 0.0) |
| { |
| SanityManager.THROWASSERT( |
| "All sums expected to be < 0.0, " + |
| "\n\tthis.cost = " + this.cost + |
| "\n\taddend.cost = " + addend.cost + |
| "\n\tsumCost = " + sumCost + |
| "\n\tthis.rowCount = " + this.rowCount + |
| "\n\taddend.rowCount = " + addend.rowCount + |
| "\n\tsumRowCount = " + sumRowCount |
| ); |
| } |
| } |
| |
| /* Presume that ordering is not maintained */ |
| return setState(sumCost, |
| sumRowCount, |
| (CostEstimateImpl) retval); |
| } |
| |
| /** @see CostEstimate#multiply */ |
| public CostEstimate multiply(double multiplicand, CostEstimate retval) { |
| if (SanityManager.DEBUG) { |
| SanityManager.ASSERT(retval == null || |
| retval instanceof CostEstimateImpl); |
| } |
| |
| double multCost = this.cost * multiplicand; |
| double multRowCount = this.rowCount * multiplicand; |
| |
| if (SanityManager.DEBUG) |
| { |
| if (multCost < 0.0 || |
| multRowCount < 0.0) |
| { |
| SanityManager.THROWASSERT( |
| "All products expected to be < 0.0, " + |
| "\n\tthis.cost = " + this.cost + |
| "\n\tmultiplicand = " + multiplicand + |
| "\n\tmultCost = " + multCost + |
| "\n\tthis.rowCount = " + this.rowCount + |
| "\n\tmultRowCount = " + multRowCount |
| ); |
| } |
| } |
| |
| /* Presume that ordering is not maintained */ |
| return setState(multCost, |
| multRowCount, |
| (CostEstimateImpl) retval); |
| } |
| |
| /** @see CostEstimate#divide */ |
| public CostEstimate divide(double divisor, CostEstimate retval) { |
| if (SanityManager.DEBUG) { |
| SanityManager.ASSERT(retval == null || |
| retval instanceof CostEstimateImpl); |
| } |
| |
| double divCost = this.cost / divisor; |
| double divRowCount = this.rowCount / divisor; |
| |
| if (SanityManager.DEBUG) |
| { |
| if (divCost < 0.0 || |
| divRowCount < 0.0) |
| { |
| SanityManager.THROWASSERT( |
| "All products expected to be < 0.0, " + |
| "\n\tthis.cost = " + this.cost + |
| "\n\tdivisor = " + divisor + |
| "\n\tdivCost = " + divCost + |
| "\n\tthis.rowCount = " + this.rowCount + |
| "\n\tdivRowCount = " + divRowCount |
| ); |
| } |
| } |
| |
| /* Presume that ordering is not maintained */ |
| return setState(divCost, |
| divRowCount, |
| (CostEstimateImpl) retval); |
| } |
| |
| /** @see CostEstimate#rowCount */ |
| public double rowCount() { |
| return rowCount; |
| } |
| |
| /** @see CostEstimate#singleScanRowCount */ |
| public double singleScanRowCount() { |
| return singleScanRowCount; |
| } |
| |
| /** @see CostEstimate#cloneMe */ |
| public CostEstimate cloneMe() { |
| return new CostEstimateImpl(cost, |
| rowCount, |
| singleScanRowCount); |
| } |
| |
| /** @see CostEstimate#isUninitialized */ |
| public boolean isUninitialized() |
| { |
| return (cost == Double.MAX_VALUE && |
| rowCount == Double.MAX_VALUE && |
| singleScanRowCount == Double.MAX_VALUE); |
| } |
| |
| /** @see StoreCostResult#getEstimatedCost */ |
| public double getEstimatedCost() { |
| return cost; |
| } |
| |
| /** @see StoreCostResult#setEstimatedCost */ |
| public void setEstimatedCost(double cost) { |
| this.cost = cost; |
| } |
| |
| /** @see StoreCostResult#getEstimatedRowCount */ |
| public long getEstimatedRowCount() { |
| return (long) rowCount; |
| } |
| |
| /** @see StoreCostResult#setEstimatedRowCount */ |
| public void setEstimatedRowCount(long count) { |
| /* This method is called by the store to |
| * give us the estimate for the # of rows |
| * returned in a scan. So, we set both |
| * rowCount and singleScanRowCount here. |
| */ |
| rowCount = (double) count; |
| singleScanRowCount = (double) count; |
| } |
| |
| CostEstimateImpl setState(double theCost, |
| double theRowCount, |
| CostEstimateImpl retval) { |
| if (retval == null) { |
| retval = new CostEstimateImpl(); |
| } |
| |
| retval.cost = theCost; |
| retval.rowCount = theRowCount; |
| |
| return retval; |
| } |
| |
| public String toString() |
| { |
| return "CostEstimateImpl: at " + hashCode() + ", cost == " + cost + |
| ", rowCount == " + rowCount + |
| ", singleScanRowCount == " + singleScanRowCount; |
| } |
| |
| } |