001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.DistinctNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.sql.compile.Optimizable;
025: import org.apache.derby.iapi.sql.compile.OptimizableList;
026: import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
027: import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
028: import org.apache.derby.iapi.sql.compile.Optimizer;
029: import org.apache.derby.iapi.sql.compile.CostEstimate;
030: import org.apache.derby.iapi.sql.compile.Visitable;
031: import org.apache.derby.iapi.sql.compile.Visitor;
032: import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
033: import org.apache.derby.iapi.sql.compile.RowOrdering;
034: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
035:
036: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
037: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
038:
039: import org.apache.derby.iapi.sql.Activation;
040: import org.apache.derby.iapi.sql.ResultSet;
041: import org.apache.derby.iapi.reference.ClassName;
042:
043: import org.apache.derby.iapi.services.classfile.VMOpcode;
044:
045: import org.apache.derby.iapi.error.StandardException;
046:
047: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
048:
049: import org.apache.derby.iapi.services.compiler.MethodBuilder;
050:
051: import org.apache.derby.iapi.services.sanity.SanityManager;
052:
053: import org.apache.derby.iapi.util.JBitSet;
054:
055: import java.util.Properties;
056: import java.util.Vector;
057:
058: /**
059: * A DistinctNode represents a result set for a disinct operation
060: * on a select. It has the same description as its input result set.
061: *
062: * For the most part, it simply delegates operations to its childResultSet,
063: * which is currently expected to be a ProjectRestrictResultSet generated
064: * for a SelectNode.
065: *
066: * NOTE: A DistinctNode extends FromTable since it can exist in a FromList.
067: *
068: * @author ames
069: */
070: public class DistinctNode extends SingleChildResultSetNode {
071: boolean inSortedOrder;
072:
073: /**
074: * Initializer for a DistinctNode.
075: *
076: * @param childResult The child ResultSetNode
077: * @param inSortedOrder Whether or not the child ResultSetNode returns its
078: * output in sorted order.
079: * @param tableProperties Properties list associated with the table
080: *
081: * @exception StandardException Thrown on error
082: */
083: public void init(Object childResult, Object inSortedOrder,
084: Object tableProperties) throws StandardException {
085: super .init(childResult, tableProperties);
086:
087: if (SanityManager.DEBUG) {
088: if (!(childResult instanceof Optimizable)) {
089: SanityManager.THROWASSERT("childResult, "
090: + childResult.getClass().getName()
091: + ", expected to be instanceof Optimizable");
092: }
093: if (!(childResult instanceof FromTable)) {
094: SanityManager.THROWASSERT("childResult, "
095: + childResult.getClass().getName()
096: + ", expected to be instanceof FromTable");
097: }
098: }
099:
100: ResultColumnList prRCList;
101:
102: /*
103: We want our own resultColumns, which are virtual columns
104: pointing to the child result's columns.
105:
106: We have to have the original object in the distinct node,
107: and give the underlying project the copy.
108: */
109:
110: /* We get a shallow copy of the ResultColumnList and its
111: * ResultColumns. (Copy maintains ResultColumn.expression for now.)
112: */
113: prRCList = this .childResult.getResultColumns()
114: .copyListAndObjects();
115: resultColumns = this .childResult.getResultColumns();
116: this .childResult.setResultColumns(prRCList);
117:
118: /* Replace ResultColumn.expression with new VirtualColumnNodes
119: * in the DistinctNode's RCL. (VirtualColumnNodes include
120: * pointers to source ResultSetNode, this, and source ResultColumn.)
121: */
122: resultColumns.genVirtualColumnNodes(this , prRCList);
123:
124: /* Verify that we can perform a DISTINCT on the
125: * underlying tree.
126: */
127: resultColumns.verifyAllOrderable();
128:
129: this .inSortedOrder = ((Boolean) inSortedOrder).booleanValue();
130: }
131:
132: /*
133: * Optimizable interface
134: */
135:
136: /**
137: * @see Optimizable#optimizeIt
138: *
139: * @exception StandardException Thrown on error
140: */
141: public CostEstimate optimizeIt(Optimizer optimizer,
142: OptimizablePredicateList predList, CostEstimate outerCost,
143: RowOrdering rowOrdering) throws StandardException {
144: CostEstimate childCost = ((Optimizable) childResult)
145: .optimizeIt(optimizer, predList, outerCost, rowOrdering);
146:
147: return super .optimizeIt(optimizer, predList, outerCost,
148: rowOrdering);
149: }
150:
151: /**
152: * @see Optimizable#estimateCost
153: *
154: * @exception StandardException Thrown on error
155: */
156: public CostEstimate estimateCost(OptimizablePredicateList predList,
157: ConglomerateDescriptor cd, CostEstimate outerCost,
158: Optimizer optimizer, RowOrdering rowOrdering)
159: throws StandardException {
160: // RESOLVE: WE NEED TO ADD IN THE COST OF SORTING HERE, AND FIGURE
161: // OUT HOW MANY ROWS WILL BE ELIMINATED.
162: CostEstimate childCost = ((Optimizable) childResult)
163: .estimateCost(predList, cd, outerCost, optimizer,
164: rowOrdering);
165:
166: costEstimate = getCostEstimate(optimizer);
167: costEstimate.setCost(childCost.getEstimatedCost(), childCost
168: .rowCount(), childCost.singleScanRowCount());
169:
170: /*
171: ** No need to use estimateCost on join strategy - that has already
172: ** been done on the child.
173: */
174: return costEstimate;
175: }
176:
177: /**
178: * @see org.apache.derby.iapi.sql.compile.Optimizable#pushOptPredicate
179: *
180: * @exception StandardException Thrown on error
181: */
182:
183: public boolean pushOptPredicate(
184: OptimizablePredicate optimizablePredicate)
185: throws StandardException {
186: return false;
187: // return ((Optimizable) childResult).pushOptPredicate(optimizablePredicate);
188: }
189:
190: /**
191: * Convert this object to a String. See comments in QueryTreeNode.java
192: * for how this should be done for tree printing.
193: *
194: * @return This object as a String
195: */
196:
197: public String toString() {
198: if (SanityManager.DEBUG) {
199: return childResult.toString() + "\n" + super .toString();
200: } else {
201: return "";
202: }
203: }
204:
205: /**
206: * Optimize this DistinctNode.
207: *
208: * @param dataDictionary The DataDictionary to use for optimization
209: * @param predicates The PredicateList to optimize. This should
210: * be a join predicate.
211: * @param outerRows The number of outer joining rows
212: *
213: * @return ResultSetNode The top of the optimized subtree
214: *
215: * @exception StandardException Thrown on error
216: */
217:
218: public ResultSetNode optimize(DataDictionary dataDictionary,
219: PredicateList predicates, double outerRows)
220: throws StandardException {
221: /* We need to implement this method since a PRN can appear above a
222: * SelectNode in a query tree.
223: */
224: childResult = (ProjectRestrictNode) childResult.optimize(
225: dataDictionary, predicates, outerRows);
226: Optimizer optimizer = getOptimizer((FromList) getNodeFactory()
227: .getNode(C_NodeTypes.FROM_LIST,
228: getNodeFactory().doJoinOrderOptimization(),
229: this , getContextManager()), predicates,
230: dataDictionary, (RequiredRowOrdering) null);
231:
232: // RESOLVE: NEED TO FACTOR IN COST OF SORTING AND FIGURE OUT HOW
233: // MANY ROWS HAVE BEEN ELIMINATED.
234: costEstimate = optimizer.newCostEstimate();
235:
236: costEstimate.setCost(childResult.getCostEstimate()
237: .getEstimatedCost(), childResult.getCostEstimate()
238: .rowCount(), childResult.getCostEstimate()
239: .singleScanRowCount());
240:
241: return this ;
242: }
243:
244: /**
245: * Return whether or not the underlying ResultSet tree
246: * is ordered on the specified columns.
247: * RESOLVE - This method currently only considers the outermost table
248: * of the query block.
249: *
250: * @param crs The specified ColumnReference[]
251: * @param permuteOrdering Whether or not the order of the CRs in the array can be permuted
252: * @param fbtVector Vector that is to be filled with the FromBaseTable
253: *
254: * @return Whether the underlying ResultSet tree
255: * is ordered on the specified column.
256: */
257: boolean isOrderedOn(ColumnReference[] crs, boolean permuteOrdering,
258: Vector fbtVector) {
259: /* RESOLVE - DistinctNodes are ordered on their RCLs.
260: * Walk RCL to see if cr is 1st non-constant column in the
261: * ordered result.
262: */
263: return false;
264: }
265:
266: /**
267: * generate the distinct result set operating over the source
268: * resultset.
269: *
270: * @exception StandardException Thrown on error
271: */
272: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
273: throws StandardException {
274: /* Get the next ResultSet#, so we can number this ResultSetNode, its
275: * ResultColumnList and ResultSet.
276: */
277: assignResultSetNumber();
278:
279: // Get the final cost estimate based on the child's cost.
280: costEstimate = childResult.getFinalCostEstimate();
281:
282: /*
283: create the orderItem and stuff it in.
284: */
285: int orderItem = acb.addItem(acb
286: .getColumnOrdering(resultColumns));
287:
288: /* Generate the SortResultSet:
289: * arg1: childExpress - Expression for childResultSet
290: * arg2: distinct - true, of course
291: * arg3: isInSortedOrder - is the source result set in sorted order
292: * arg4: orderItem - entry in saved objects for the ordering
293: * arg5: rowAllocator - method to construct rows for fetching
294: * from the sort
295: * arg6: row size
296: * arg7: resultSetNumber
297: */
298:
299: acb.pushGetResultSetFactoryExpression(mb);
300:
301: childResult.generate(acb, mb);
302: mb.push(true);
303: mb.push(inSortedOrder);
304: mb.push(orderItem);
305: resultColumns.generateHolder(acb, mb);
306: mb.push(resultColumns.getTotalColumnSize());
307: mb.push(resultSetNumber);
308: mb.push(costEstimate.rowCount());
309: mb.push(costEstimate.getEstimatedCost());
310:
311: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
312: "getSortResultSet", ClassName.NoPutResultSet, 9);
313: }
314: }
|