| /* |
| |
| Derby - Class org.apache.derby.impl.sql.compile.DistinctNode |
| |
| 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 java.util.List; |
| import java.util.Properties; |
| import org.apache.derby.iapi.error.StandardException; |
| import org.apache.derby.iapi.reference.ClassName; |
| import org.apache.derby.iapi.services.classfile.VMOpcode; |
| import org.apache.derby.iapi.services.compiler.MethodBuilder; |
| import org.apache.derby.iapi.services.context.ContextManager; |
| import org.apache.derby.shared.common.sanity.SanityManager; |
| import org.apache.derby.iapi.sql.compile.CostEstimate; |
| import org.apache.derby.iapi.sql.compile.Optimizable; |
| import org.apache.derby.iapi.sql.compile.OptimizablePredicate; |
| import org.apache.derby.iapi.sql.compile.OptimizablePredicateList; |
| import org.apache.derby.iapi.sql.compile.Optimizer; |
| import org.apache.derby.iapi.sql.compile.RequiredRowOrdering; |
| import org.apache.derby.iapi.sql.compile.RowOrdering; |
| import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor; |
| import org.apache.derby.iapi.sql.dictionary.DataDictionary; |
| |
| /** |
| * A DistinctNode represents a result set for a distinct operation |
| * on a select. It has the same description as its input result set. |
| * |
| * For the most part, it simply delegates operations to its childResultSet, |
| * which is currently expected to be a ProjectRestrictResultSet generated |
| * for a SelectNode. |
| * |
| * NOTE: A DistinctNode extends FromTable since it can exist in a FromList. |
| * |
| */ |
| class DistinctNode extends SingleChildResultSetNode |
| { |
| boolean inSortedOrder; |
| |
| /** |
| * Constructor for a DistinctNode. |
| * |
| * @param childResult The child ResultSetNode |
| * @param inSortedOrder Whether or not the child ResultSetNode returns its |
| * output in sorted order. |
| * @param tableProperties Properties list associated with the table |
| * @param cm The context manager |
| * |
| * @exception StandardException Thrown on error |
| */ |
| DistinctNode(ResultSetNode childResult, |
| boolean inSortedOrder, |
| Properties tableProperties, |
| ContextManager cm) throws StandardException |
| { |
| super(childResult, tableProperties, cm); |
| |
| if (SanityManager.DEBUG) |
| { |
| if (!(childResult instanceof Optimizable)) |
| { |
| SanityManager.THROWASSERT("childResult, " + childResult.getClass().getName() + |
| ", expected to be instanceof Optimizable"); |
| } |
| if (!(childResult instanceof FromTable)) |
| { |
| SanityManager.THROWASSERT("childResult, " + childResult.getClass().getName() + |
| ", expected to be instanceof FromTable"); |
| } |
| } |
| |
| /* |
| We want our own resultColumns, which are virtual columns |
| pointing to the child result's columns. |
| |
| We have to have the original object in the distinct node, |
| and give the underlying project the copy. |
| */ |
| |
| /* We get a shallow copy of the ResultColumnList and its |
| * ResultColumns. (Copy maintains ResultColumn.expression for now.) |
| */ |
| final ResultColumnList prRCList = |
| this.childResult.getResultColumns().copyListAndObjects(); |
| setResultColumns( this.childResult.getResultColumns() ); |
| this.childResult.setResultColumns(prRCList); |
| |
| /* Replace ResultColumn.expression with new VirtualColumnNodes |
| * in the DistinctNode's RCL. (VirtualColumnNodes include |
| * pointers to source ResultSetNode, this, and source ResultColumn.) |
| */ |
| getResultColumns().genVirtualColumnNodes(this, prRCList); |
| |
| /* Verify that we can perform a DISTINCT on the |
| * underlying tree. |
| */ |
| getResultColumns().verifyAllOrderable(); |
| this.inSortedOrder = inSortedOrder; |
| } |
| |
| /* |
| * Optimizable interface |
| */ |
| |
| /** |
| * @see Optimizable#optimizeIt |
| * |
| * @exception StandardException Thrown on error |
| */ |
| @Override |
| public CostEstimate optimizeIt(Optimizer optimizer, |
| OptimizablePredicateList predList, |
| CostEstimate outerCost, |
| RowOrdering rowOrdering) |
| throws StandardException |
| { |
| ((Optimizable) childResult).optimizeIt(optimizer, |
| predList, |
| outerCost, |
| rowOrdering); |
| |
| return super.optimizeIt(optimizer, predList, outerCost, rowOrdering); |
| } |
| |
| /** |
| * @see Optimizable#estimateCost |
| * |
| * @exception StandardException Thrown on error |
| */ |
| @Override |
| public CostEstimate estimateCost(OptimizablePredicateList predList, |
| ConglomerateDescriptor cd, |
| CostEstimate outerCost, |
| Optimizer optimizer, |
| RowOrdering rowOrdering) |
| throws StandardException |
| { |
| // RESOLVE: WE NEED TO ADD IN THE COST OF SORTING HERE, AND FIGURE |
| // OUT HOW MANY ROWS WILL BE ELIMINATED. |
| CostEstimate childCost = |
| ((Optimizable) childResult).estimateCost(predList, |
| cd, |
| outerCost, |
| optimizer, |
| rowOrdering); |
| |
| setCostEstimate( getCostEstimate(optimizer) ); |
| getCostEstimate().setCost(childCost.getEstimatedCost(), |
| childCost.rowCount(), |
| childCost.singleScanRowCount()); |
| |
| |
| /* |
| ** No need to use estimateCost on join strategy - that has already |
| ** been done on the child. |
| */ |
| return getCostEstimate(); |
| } |
| |
| /** |
| * @see org.apache.derby.iapi.sql.compile.Optimizable#pushOptPredicate |
| * |
| * @exception StandardException Thrown on error |
| */ |
| @Override |
| public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate) |
| throws StandardException |
| { |
| return false; |
| // return ((Optimizable) childResult).pushOptPredicate(optimizablePredicate); |
| } |
| |
| |
| /** |
| * Optimize this DistinctNode. |
| * |
| * @param dataDictionary The DataDictionary to use for optimization |
| * @param predicates The PredicateList to optimize. This should |
| * be a join predicate. |
| * @param outerRows The number of outer joining rows |
| * |
| * @return ResultSetNode The top of the optimized subtree |
| * |
| * @exception StandardException Thrown on error |
| */ |
| @Override |
| ResultSetNode optimize(DataDictionary dataDictionary, |
| PredicateList predicates, |
| double outerRows) |
| throws StandardException |
| { |
| /* We need to implement this method since a PRN can appear above a |
| * SelectNode in a query tree. |
| */ |
| childResult = childResult.optimize(dataDictionary, |
| predicates, |
| outerRows); |
| |
| // RESOLVE: NEED TO FACTOR IN COST OF SORTING AND FIGURE OUT HOW |
| // MANY ROWS HAVE BEEN ELIMINATED. |
| setCostEstimate( getOptimizerFactory().getCostEstimate() ); |
| |
| getCostEstimate().setCost(childResult.getCostEstimate().getEstimatedCost(), |
| childResult.getCostEstimate().rowCount(), |
| childResult.getCostEstimate().singleScanRowCount()); |
| |
| return this; |
| } |
| |
| /** |
| * Return whether or not the underlying ResultSet tree |
| * is ordered on the specified columns. |
| * RESOLVE - This method currently only considers the outermost table |
| * of the query block. |
| * |
| * @param crs The specified ColumnReference[] |
| * @param permuteOrdering Whether or not the order of the CRs in the array can be permuted |
| * @param fbtHolder List that is to be filled with the FromBaseTable |
| * |
| * @return Whether the underlying ResultSet tree |
| * is ordered on the specified column. |
| */ |
| @Override |
| boolean isOrderedOn(ColumnReference[] crs, |
| boolean permuteOrdering, |
| List<FromBaseTable> fbtHolder) |
| { |
| /* RESOLVE - DistinctNodes are ordered on their RCLs. |
| * Walk RCL to see if cr is 1st non-constant column in the |
| * ordered result. |
| */ |
| return false; |
| } |
| |
| /** |
| * generate the distinct result set operating over the source |
| * result set. |
| * |
| * @exception StandardException Thrown on error |
| */ |
| @Override |
| void generate(ActivationClassBuilder acb, MethodBuilder mb) |
| throws StandardException |
| { |
| /* Get the next ResultSet#, so we can number this ResultSetNode, its |
| * ResultColumnList and ResultSet. |
| */ |
| assignResultSetNumber(); |
| |
| // Get the final cost estimate based on the child's cost. |
| setCostEstimate( childResult.getFinalCostEstimate() ); |
| |
| /* |
| create the orderItem and stuff it in. |
| */ |
| int orderItem = acb.addItem(acb.getColumnOrdering(getResultColumns())); |
| |
| /* Generate the SortResultSet: |
| * arg1: childExpress - Expression for childResultSet |
| * arg2: distinct - true, of course |
| * arg3: isInSortedOrder - is the source result set in sorted order |
| * arg4: orderItem - entry in saved objects for the ordering |
| * arg5: rowAllocator - method to construct rows for fetching |
| * from the sort |
| * arg6: row size |
| * arg7: resultSetNumber |
| */ |
| |
| acb.pushGetResultSetFactoryExpression(mb); |
| |
| childResult.generate(acb, mb); |
| mb.push(true); |
| mb.push(inSortedOrder); |
| mb.push(orderItem); |
| mb.push(acb.addItem(getResultColumns().buildRowTemplate())); |
| mb.push(getResultColumns().getTotalColumnSize()); |
| mb.push(getResultSetNumber()); |
| mb.push(getCostEstimate().rowCount()); |
| mb.push(getCostEstimate().getEstimatedCost()); |
| |
| mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getSortResultSet", |
| ClassName.NoPutResultSet, 9); |
| } |
| } |