001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.HashTableNode
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.services.context.ContextManager;
025:
026: import org.apache.derby.iapi.sql.compile.Optimizable;
027: import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
028: import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
029: import org.apache.derby.iapi.sql.compile.Optimizer;
030: import org.apache.derby.iapi.sql.compile.CostEstimate;
031: import org.apache.derby.iapi.sql.compile.OptimizableList;
032: import org.apache.derby.iapi.sql.compile.Visitable;
033: import org.apache.derby.iapi.sql.compile.Visitor;
034: import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
035: import org.apache.derby.iapi.sql.compile.RowOrdering;
036: import org.apache.derby.iapi.sql.compile.AccessPath;
037: import org.apache.derby.iapi.reference.ClassName;
038:
039: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
040: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
041:
042: import org.apache.derby.iapi.sql.Activation;
043: import org.apache.derby.iapi.sql.ResultSet;
044:
045: import org.apache.derby.iapi.error.StandardException;
046:
047: import org.apache.derby.iapi.store.access.TransactionController;
048:
049: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
050: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
051:
052: import org.apache.derby.iapi.services.compiler.MethodBuilder;
053:
054: import org.apache.derby.iapi.services.loader.GeneratedMethod;
055:
056: import org.apache.derby.iapi.services.sanity.SanityManager;
057:
058: import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
059:
060: import org.apache.derby.iapi.services.io.FormatableArrayHolder;
061: import org.apache.derby.iapi.services.io.FormatableIntHolder;
062: import org.apache.derby.iapi.util.JBitSet;
063: import org.apache.derby.iapi.services.classfile.VMOpcode;
064:
065: import java.util.Properties;
066:
067: /**
068: * A HashTableNode represents a result set where a hash table is built.
069: *
070: * @author Jerry Brenner
071: */
072:
073: public class HashTableNode extends SingleChildResultSetNode {
074: PredicateList searchPredicateList;
075: PredicateList joinPredicateList;
076:
077: SubqueryList pSubqueryList;
078: SubqueryList rSubqueryList;
079:
080: /**
081: * Initializer for a HashTableNode.
082: *
083: * @param childResult The child result set
084: * @param tableProperties Properties list associated with the table
085: * @param resultColumns The RCL.
086: * @param searchPredicateList Single table clauses
087: * @param joinPredicateList Multi table clauses
088: * @param accessPath The access path
089: * @param costEstimate The cost estimate
090: * @param pSubqueryList List of subqueries in RCL
091: * @param rSubqueryList List of subqueries in Predicate lists
092: * @param hashKeyColumns Hash key columns
093: */
094:
095: public void init(Object childResult, Object tableProperties,
096: Object resultColumns, Object searchPredicateList,
097: Object joinPredicateList, Object accessPath,
098: Object costEstimate, Object pSubqueryList,
099: Object rSubqueryList, Object hashKeyColumns) {
100: super .init(childResult, tableProperties);
101: this .resultColumns = (ResultColumnList) resultColumns;
102: this .searchPredicateList = (PredicateList) searchPredicateList;
103: this .joinPredicateList = (PredicateList) joinPredicateList;
104: this .trulyTheBestAccessPath = (AccessPathImpl) accessPath;
105: this .costEstimate = (CostEstimate) costEstimate;
106: this .pSubqueryList = (SubqueryList) pSubqueryList;
107: this .rSubqueryList = (SubqueryList) rSubqueryList;
108: setHashKeyColumns((int[]) hashKeyColumns);
109: }
110:
111: /*
112: * Optimizable interface
113: */
114:
115: /**
116: * @see Optimizable#modifyAccessPath
117: *
118: * @exception StandardException Thrown on error
119: */
120: public Optimizable modifyAccessPath(JBitSet outerTables,
121: Optimizer optimizer) throws StandardException {
122: return this ;
123: }
124:
125: /**
126: * Prints the sub-nodes of this object. See QueryTreeNode.java for
127: * how tree printing is supposed to work.
128: *
129: * @param depth The depth of this node in the tree
130: */
131:
132: public void printSubNodes(int depth) {
133: if (SanityManager.DEBUG) {
134: super .printSubNodes(depth);
135:
136: if (searchPredicateList != null) {
137: printLabel(depth, "searchPredicateList: ");
138: searchPredicateList.treePrint(depth + 1);
139: }
140:
141: if (joinPredicateList != null) {
142: printLabel(depth, "joinPredicateList: ");
143: joinPredicateList.treePrint(depth + 1);
144: }
145: }
146: }
147:
148: /**
149: * For joins, the tree will be (nodes are left out if the clauses
150: * are empty):
151: *
152: * ProjectRestrictResultSet -- for the having and the select list
153: * SortResultSet -- for the group by list
154: * ProjectRestrictResultSet -- for the where and the select list (if no group or having)
155: * the result set for the fromList
156: *
157: *
158: * @exception StandardException Thrown on error
159: */
160: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
161: throws StandardException {
162: if (SanityManager.DEBUG)
163: SanityManager.ASSERT(resultColumns != null,
164: "Tree structure bad");
165:
166: generateMinion(acb, mb, false);
167: }
168:
169: /**
170: * General logic shared by Core compilation and by the Replication Filter
171: * compiler. A couple ResultSets (the ones used by PREPARE SELECT FILTER)
172: * implement this method.
173: *
174: * @param acb The ExpressionClassBuilder for the class being built
175: * @param mb the method the expression will go into
176: *
177: *
178: * @exception StandardException Thrown on error
179: */
180:
181: public void generateResultSet(ExpressionClassBuilder acb,
182: MethodBuilder mb) throws StandardException {
183: generateMinion(acb, mb, true);
184: }
185:
186: /**
187: * Logic shared by generate() and generateResultSet().
188: *
189: * @param acb The ExpressionClassBuilder for the class being built
190: * @param mb the method the expression will go into
191: *
192: * @exception StandardException Thrown on error
193: */
194:
195: private void generateMinion(ExpressionClassBuilder acb,
196: MethodBuilder mb, boolean genChildResultSet)
197: throws StandardException {
198: MethodBuilder userExprFun;
199: ValueNode searchClause = null;
200: ValueNode equijoinClause = null;
201:
202: /* The tableProperties, if non-null, must be correct to get this far.
203: * We simply call verifyProperties to set initialCapacity and
204: * loadFactor.
205: */
206: verifyProperties(getDataDictionary());
207:
208: // build up the tree.
209:
210: /* Put the predicates back into the tree */
211: if (searchPredicateList != null) {
212: // Remove any redundant predicates before restoring
213: searchPredicateList.removeRedundantPredicates();
214: searchClause = searchPredicateList.restorePredicates();
215: /* Allow the searchPredicateList to get garbage collected now
216: * that we're done with it.
217: */
218: searchPredicateList = null;
219: }
220:
221: // for the single table predicates, we generate an exprFun
222: // that evaluates the expression of the clause
223: // against the current row of the child's result.
224: // if the restriction is empty, simply pass null
225: // to optimize for run time performance.
226:
227: // generate the function and initializer:
228: // Note: Boolean lets us return nulls (boolean would not)
229: // private Boolean exprN()
230: // {
231: // return <<searchClause.generate(ps)>>;
232: // }
233: // static Method exprN = method pointer to exprN;
234:
235: // Map the result columns to the source columns
236: int[] mapArray = resultColumns.mapSourceColumns();
237: int mapArrayItem = acb
238: .addItem(new ReferencedColumnsDescriptorImpl(mapArray));
239:
240: // Save the hash key columns
241:
242: FormatableIntHolder[] fihArray = FormatableIntHolder
243: .getFormatableIntHolders(hashKeyColumns());
244: FormatableArrayHolder hashKeyHolder = new FormatableArrayHolder(
245: fihArray);
246: int hashKeyItem = acb.addItem(hashKeyHolder);
247:
248: /* Generate the HashTableResultSet:
249: * arg1: childExpress - Expression for childResultSet
250: * arg2: searchExpress - Expression for single table predicates
251: * arg3 : equijoinExpress - Qualifier[] for hash table look up
252: * arg4: projectExpress - Expression for projection, if any
253: * arg5: resultSetNumber
254: * arg6: mapArrayItem - item # for mapping of source columns
255: * arg7: reuseResult - whether or not the result row can be reused
256: * (ie, will it always be the same)
257: * arg8: hashKeyItem - item # for int[] of hash column #s
258: * arg9: removeDuplicates - don't remove duplicates in hash table (for now)
259: * arg10: maxInMemoryRowCount - max row size for in-memory hash table
260: * arg11: initialCapacity - initialCapacity for java.util.Hashtable
261: * arg12 : loadFactor - loadFactor for java.util.Hashtable
262: * arg13: estimated row count
263: * arg14: estimated cost
264: * arg15: close method
265: */
266:
267: acb.pushGetResultSetFactoryExpression(mb);
268:
269: if (genChildResultSet)
270: childResult.generateResultSet(acb, mb);
271: else
272: childResult.generate((ActivationClassBuilder) acb, mb);
273:
274: /* Get the next ResultSet #, so that we can number this ResultSetNode, its
275: * ResultColumnList and ResultSet.
276: */
277: assignResultSetNumber();
278:
279: /* Set the point of attachment in all subqueries attached
280: * to this node.
281: */
282: if (pSubqueryList != null && pSubqueryList.size() > 0) {
283: pSubqueryList.setPointOfAttachment(resultSetNumber);
284: if (SanityManager.DEBUG) {
285: SanityManager.ASSERT(pSubqueryList.size() == 0,
286: "pSubqueryList.size() expected to be 0");
287: }
288: }
289: if (rSubqueryList != null && rSubqueryList.size() > 0) {
290: rSubqueryList.setPointOfAttachment(resultSetNumber);
291: if (SanityManager.DEBUG) {
292: SanityManager.ASSERT(rSubqueryList.size() == 0,
293: "rSubqueryList.size() expected to be 0");
294: }
295: }
296:
297: // Get the final cost estimate based on child's cost.
298: costEstimate = childResult.getFinalCostEstimate();
299:
300: // if there is no searchClause, we just want to pass null.
301: if (searchClause == null) {
302: mb.pushNull(ClassName.GeneratedMethod);
303: } else {
304: // this sets up the method and the static field.
305: // generates:
306: // DataValueDescriptor userExprFun { }
307: userExprFun = acb.newUserExprFun();
308:
309: // searchClause knows it is returning its value;
310:
311: /* generates:
312: * return <searchClause.generate(acb)>;
313: * and adds it to userExprFun
314: * NOTE: The explicit cast to DataValueDescriptor is required
315: * since the searchClause may simply be a boolean column or subquery
316: * which returns a boolean. For example:
317: * where booleanColumn
318: */
319:
320: searchClause.generateExpression(acb, userExprFun);
321: userExprFun.methodReturn();
322:
323: /* PUSHCOMPILER
324: userSB.newReturnStatement(searchClause.generateExpression(acb, userSB));
325: */
326:
327: // we are done modifying userExprFun, complete it.
328: userExprFun.complete();
329:
330: // searchClause is used in the final result set as an access of the new static
331: // field holding a reference to this new method.
332: // generates:
333: // ActivationClass.userExprFun
334: // which is the static field that "points" to the userExprFun
335: // that evaluates the where clause.
336: acb.pushMethodReference(mb, userExprFun);
337: }
338: /* Generate the qualifiers for the look up into
339: * the hash table.
340: */
341: joinPredicateList.generateQualifiers(acb, mb,
342: (Optimizable) childResult, false);
343:
344: /* Determine whether or not reflection is needed for the projection.
345: * Reflection is not needed if all of the columns map directly to source
346: * columns.
347: */
348: if (reflectionNeededForProjection()) {
349: // for the resultColumns, we generate a userExprFun
350: // that creates a new row from expressions against
351: // the current row of the child's result.
352: // (Generate optimization: see if we can simply
353: // return the current row -- we could, but don't, optimize
354: // the function call out and have execution understand
355: // that a null function pointer means take the current row
356: // as-is, with the performance trade-off as discussed above.)
357:
358: /* Generate the Row function for the projection */
359: resultColumns.generateCore(acb, mb, false);
360: } else {
361: mb.pushNull(ClassName.GeneratedMethod);
362: }
363:
364: mb.push(resultSetNumber);
365: mb.push(mapArrayItem);
366: mb.push(resultColumns.reusableResult());
367: mb.push(hashKeyItem);
368: mb.push(false);
369: mb.push(-1L);
370: mb.push(initialCapacity);
371: mb.push(loadFactor);
372: mb.push(costEstimate.singleScanRowCount());
373: mb.push(costEstimate.getEstimatedCost());
374:
375: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
376: "getHashTableResultSet", ClassName.NoPutResultSet, 14);
377: }
378:
379: /**
380: * Accept a visitor, and call v.visit()
381: * on child nodes as necessary.
382: *
383: * @param v the visitor
384: *
385: * @exception StandardException on error
386: */
387: public Visitable accept(Visitor v) throws StandardException {
388: if (v.skipChildren(this )) {
389: return v.visit(this );
390: }
391:
392: Visitable returnNode = super .accept(v);
393:
394: if (searchPredicateList != null && !v.stopTraversal()) {
395: searchPredicateList = (PredicateList) searchPredicateList
396: .accept(v);
397: }
398:
399: if (joinPredicateList != null && !v.stopTraversal()) {
400: joinPredicateList = (PredicateList) joinPredicateList
401: .accept(v);
402: }
403:
404: return returnNode;
405: }
406: }
|