--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.DistinctNode\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.compile;\r
+\r
+import org.apache.derby.iapi.sql.compile.Optimizable;\r
+import org.apache.derby.iapi.sql.compile.OptimizableList;\r
+import org.apache.derby.iapi.sql.compile.OptimizablePredicate;\r
+import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;\r
+import org.apache.derby.iapi.sql.compile.Optimizer;\r
+import org.apache.derby.iapi.sql.compile.CostEstimate;\r
+import org.apache.derby.iapi.sql.compile.Visitable;\r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;\r
+import org.apache.derby.iapi.sql.compile.RowOrdering;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+import org.apache.derby.iapi.reference.ClassName;\r
+\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+\r
+\r
+import java.util.Properties;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * A DistinctNode represents a result set for a disinct operation\r
+ * on a select. It has the same description as its input result set.\r
+ *\r
+ * For the most part, it simply delegates operations to its childResultSet,\r
+ * which is currently expected to be a ProjectRestrictResultSet generated\r
+ * for a SelectNode.\r
+ *\r
+ * NOTE: A DistinctNode extends FromTable since it can exist in a FromList.\r
+ *\r
+ */\r
+public class DistinctNode extends SingleChildResultSetNode\r
+{\r
+ boolean inSortedOrder;\r
+\r
+ /**\r
+ * Initializer for a DistinctNode.\r
+ *\r
+ * @param childResult The child ResultSetNode\r
+ * @param inSortedOrder Whether or not the child ResultSetNode returns its\r
+ * output in sorted order.\r
+ * @param tableProperties Properties list associated with the table\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void init(\r
+ Object childResult,\r
+ Object inSortedOrder,\r
+ Object tableProperties) throws StandardException\r
+ {\r
+ super.init(childResult, tableProperties);\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (!(childResult instanceof Optimizable))\r
+ {\r
+ SanityManager.THROWASSERT("childResult, " + childResult.getClass().getName() +\r
+ ", expected to be instanceof Optimizable");\r
+ }\r
+ if (!(childResult instanceof FromTable))\r
+ {\r
+ SanityManager.THROWASSERT("childResult, " + childResult.getClass().getName() +\r
+ ", expected to be instanceof FromTable");\r
+ }\r
+ }\r
+\r
+ ResultColumnList prRCList;\r
+\r
+ /*\r
+ We want our own resultColumns, which are virtual columns\r
+ pointing to the child result's columns.\r
+\r
+ We have to have the original object in the distinct node,\r
+ and give the underlying project the copy.\r
+ */\r
+\r
+ /* We get a shallow copy of the ResultColumnList and its \r
+ * ResultColumns. (Copy maintains ResultColumn.expression for now.)\r
+ */\r
+ prRCList = this.childResult.getResultColumns().copyListAndObjects();\r
+ resultColumns = this.childResult.getResultColumns();\r
+ this.childResult.setResultColumns(prRCList);\r
+\r
+ /* Replace ResultColumn.expression with new VirtualColumnNodes\r
+ * in the DistinctNode's RCL. (VirtualColumnNodes include\r
+ * pointers to source ResultSetNode, this, and source ResultColumn.)\r
+ */\r
+ resultColumns.genVirtualColumnNodes(this, prRCList);\r
+\r
+ /* Verify that we can perform a DISTINCT on the\r
+ * underlying tree.\r
+ */\r
+ resultColumns.verifyAllOrderable();\r
+\r
+ this.inSortedOrder = ((Boolean) inSortedOrder).booleanValue();\r
+ }\r
+\r
+ /*\r
+ * Optimizable interface\r
+ */\r
+\r
+ /**\r
+ * @see Optimizable#optimizeIt\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public CostEstimate optimizeIt(Optimizer optimizer,\r
+ OptimizablePredicateList predList,\r
+ CostEstimate outerCost,\r
+ RowOrdering rowOrdering)\r
+ throws StandardException\r
+ {\r
+ CostEstimate childCost =\r
+ ((Optimizable) childResult).optimizeIt(optimizer,\r
+ predList,\r
+ outerCost,\r
+ rowOrdering);\r
+\r
+ return super.optimizeIt(optimizer, predList, outerCost, rowOrdering);\r
+ }\r
+\r
+ /**\r
+ * @see Optimizable#estimateCost\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public CostEstimate estimateCost(OptimizablePredicateList predList,\r
+ ConglomerateDescriptor cd,\r
+ CostEstimate outerCost,\r
+ Optimizer optimizer,\r
+ RowOrdering rowOrdering)\r
+ throws StandardException\r
+ {\r
+ // RESOLVE: WE NEED TO ADD IN THE COST OF SORTING HERE, AND FIGURE\r
+ // OUT HOW MANY ROWS WILL BE ELIMINATED.\r
+ CostEstimate childCost =\r
+ ((Optimizable) childResult).estimateCost(predList,\r
+ cd,\r
+ outerCost,\r
+ optimizer,\r
+ rowOrdering);\r
+\r
+ costEstimate = getCostEstimate(optimizer);\r
+ costEstimate.setCost(childCost.getEstimatedCost(),\r
+ childCost.rowCount(),\r
+ childCost.singleScanRowCount());\r
+\r
+\r
+ /*\r
+ ** No need to use estimateCost on join strategy - that has already\r
+ ** been done on the child.\r
+ */\r
+ return costEstimate;\r
+ }\r
+\r
+ /**\r
+ * @see org.apache.derby.iapi.sql.compile.Optimizable#pushOptPredicate\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate)\r
+ throws StandardException\r
+ {\r
+ return false;\r
+ // return ((Optimizable) childResult).pushOptPredicate(optimizablePredicate);\r
+ }\r
+\r
+ /**\r
+ * Convert this object to a String. See comments in QueryTreeNode.java\r
+ * for how this should be done for tree printing.\r
+ *\r
+ * @return This object as a String\r
+ */\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ return childResult.toString() + "\n" + super.toString();\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Optimize this DistinctNode. \r
+ *\r
+ * @param dataDictionary The DataDictionary to use for optimization\r
+ * @param predicates The PredicateList to optimize. This should\r
+ * be a join predicate.\r
+ * @param outerRows The number of outer joining rows\r
+ *\r
+ * @return ResultSetNode The top of the optimized subtree\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public ResultSetNode optimize(DataDictionary dataDictionary,\r
+ PredicateList predicates,\r
+ double outerRows) \r
+ throws StandardException\r
+ {\r
+ /* We need to implement this method since a PRN can appear above a\r
+ * SelectNode in a query tree.\r
+ */\r
+ childResult = (ProjectRestrictNode) childResult.optimize(\r
+ dataDictionary,\r
+ predicates,\r
+ outerRows);\r
+ Optimizer optimizer = getOptimizer(\r
+ (FromList) getNodeFactory().getNode(\r
+ C_NodeTypes.FROM_LIST,\r
+ getNodeFactory().doJoinOrderOptimization(),\r
+ this,\r
+ getContextManager()),\r
+ predicates,\r
+ dataDictionary,\r
+ (RequiredRowOrdering) null);\r
+\r
+ // RESOLVE: NEED TO FACTOR IN COST OF SORTING AND FIGURE OUT HOW\r
+ // MANY ROWS HAVE BEEN ELIMINATED.\r
+ costEstimate = optimizer.newCostEstimate();\r
+\r
+ costEstimate.setCost(childResult.getCostEstimate().getEstimatedCost(),\r
+ childResult.getCostEstimate().rowCount(),\r
+ childResult.getCostEstimate().singleScanRowCount());\r
+\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not the underlying ResultSet tree\r
+ * is ordered on the specified columns.\r
+ * RESOLVE - This method currently only considers the outermost table \r
+ * of the query block.\r
+ *\r
+ * @param crs The specified ColumnReference[]\r
+ * @param permuteOrdering Whether or not the order of the CRs in the array can be permuted\r
+ * @param fbtVector Vector that is to be filled with the FromBaseTable \r
+ *\r
+ * @return Whether the underlying ResultSet tree\r
+ * is ordered on the specified column.\r
+ */\r
+ boolean isOrderedOn(ColumnReference[] crs, boolean permuteOrdering, Vector fbtVector)\r
+ {\r
+ /* RESOLVE - DistinctNodes are ordered on their RCLs.\r
+ * Walk RCL to see if cr is 1st non-constant column in the\r
+ * ordered result.\r
+ */\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * generate the distinct result set operating over the source\r
+ * resultset.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void generate(ActivationClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ /* Get the next ResultSet#, so we can number this ResultSetNode, its\r
+ * ResultColumnList and ResultSet.\r
+ */\r
+ assignResultSetNumber();\r
+\r
+ // Get the final cost estimate based on the child's cost.\r
+ costEstimate = childResult.getFinalCostEstimate();\r
+\r
+ /*\r
+ create the orderItem and stuff it in.\r
+ */\r
+ int orderItem = acb.addItem(acb.getColumnOrdering(resultColumns));\r
+\r
+ /* Generate the SortResultSet:\r
+ * arg1: childExpress - Expression for childResultSet\r
+ * arg2: distinct - true, of course\r
+ * arg3: isInSortedOrder - is the source result set in sorted order\r
+ * arg4: orderItem - entry in saved objects for the ordering\r
+ * arg5: rowAllocator - method to construct rows for fetching\r
+ * from the sort\r
+ * arg6: row size\r
+ * arg7: resultSetNumber\r
+ */\r
+\r
+ acb.pushGetResultSetFactoryExpression(mb);\r
+\r
+ childResult.generate(acb, mb);\r
+ mb.push(true);\r
+ mb.push(inSortedOrder);\r
+ mb.push(orderItem);\r
+ resultColumns.generateHolder(acb, mb);\r
+ mb.push(resultColumns.getTotalColumnSize());\r
+ mb.push(resultSetNumber);\r
+ mb.push(costEstimate.rowCount());\r
+ mb.push(costEstimate.getEstimatedCost());\r
+\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getSortResultSet",\r
+ ClassName.NoPutResultSet, 9);\r
+ }\r
+}\r