001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.TableOperatorNode
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.services.sanity.SanityManager;
027:
028: import org.apache.derby.iapi.error.StandardException;
029:
030: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
031: import org.apache.derby.iapi.sql.compile.CompilerContext;
032: import org.apache.derby.iapi.sql.compile.Optimizable;
033: import org.apache.derby.iapi.sql.compile.Visitable;
034: import org.apache.derby.iapi.sql.compile.Visitor;
035: import org.apache.derby.iapi.sql.compile.Optimizer;
036: import org.apache.derby.iapi.sql.compile.OptimizableList;
037: import org.apache.derby.iapi.sql.compile.CostEstimate;
038: import org.apache.derby.iapi.sql.compile.OptimizerFactory;
039: import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
040: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
041:
042: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
043: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
044:
045: import org.apache.derby.iapi.util.JBitSet;
046:
047: import java.util.Properties;
048:
049: /**
050: * A TableOperatorNode represents a relational operator like UNION, INTERSECT,
051: * JOIN, etc. that takes two tables as parameters and returns a table. The
052: * parameters it takes are represented as ResultSetNodes.
053: *
054: * Currently, all known table operators are binary operators, so there are no
055: * subclasses of this node type called "BinaryTableOperatorNode" and
056: * "UnaryTableOperatorNode".
057: *
058: * @author Jeff Lichtman
059: */
060:
061: abstract class TableOperatorNode extends FromTable {
062: ResultSetNode leftResultSet;
063: ResultSetNode rightResultSet;
064: Optimizer leftOptimizer;
065: Optimizer rightOptimizer;
066: private boolean leftModifyAccessPathsDone;
067: private boolean rightModifyAccessPathsDone;
068:
069: /**
070: * Initializer for a TableOperatorNode.
071: *
072: * @param leftResultSet The ResultSetNode on the left side of this node
073: * @param rightResultSet The ResultSetNode on the right side of this node
074: * @param tableProperties Properties list associated with the table
075: *
076: * @exception StandardException Thrown on error
077: */
078: public void init(Object leftResultSet, Object rightResultSet,
079: Object tableProperties) throws StandardException {
080: /* correlationName is always null */
081: init(null, tableProperties);
082: this .leftResultSet = (ResultSetNode) leftResultSet;
083: this .rightResultSet = (ResultSetNode) rightResultSet;
084: }
085:
086: /**
087: * @see Optimizable#modifyAccessPath
088: *
089: * @exception StandardException Thrown on error
090: */
091: public Optimizable modifyAccessPath(JBitSet outerTables)
092: throws StandardException {
093: boolean callModifyAccessPaths = false;
094:
095: if (leftResultSet instanceof FromTable) {
096: if (leftOptimizer != null)
097: leftOptimizer.modifyAccessPaths();
098: else {
099: leftResultSet = (ResultSetNode) ((FromTable) leftResultSet)
100: .modifyAccessPath(outerTables);
101: }
102: leftModifyAccessPathsDone = true;
103: } else {
104: callModifyAccessPaths = true;
105: }
106:
107: if (rightResultSet instanceof FromTable) {
108: if (rightOptimizer != null)
109: rightOptimizer.modifyAccessPaths();
110: else {
111: rightResultSet = (ResultSetNode) ((FromTable) rightResultSet)
112: .modifyAccessPath(outerTables);
113: }
114: rightModifyAccessPathsDone = true;
115: } else {
116: callModifyAccessPaths = true;
117: }
118:
119: if (callModifyAccessPaths) {
120: return (Optimizable) modifyAccessPaths();
121: }
122: return this ;
123: }
124:
125: /** @see Optimizable#verifyProperties
126: * @exception StandardException Thrown on error
127: */
128: public void verifyProperties(DataDictionary dDictionary)
129: throws StandardException {
130: if (leftResultSet instanceof Optimizable) {
131: ((Optimizable) leftResultSet).verifyProperties(dDictionary);
132: }
133: if (rightResultSet instanceof Optimizable) {
134: ((Optimizable) rightResultSet)
135: .verifyProperties(dDictionary);
136: }
137:
138: super .verifyProperties(dDictionary);
139: }
140:
141: /**
142: * @see Optimizable#updateBestPlanMap
143: *
144: * Makes a call to add/load/remove the plan mapping for this node,
145: * and then makes the necessary call to recurse on this node's
146: * left and right child, in order to ensure that we've handled
147: * the full plan all the way down this node's subtree.
148: */
149: public void updateBestPlanMap(short action, Object planKey)
150: throws StandardException {
151: super .updateBestPlanMap(action, planKey);
152:
153: // Now walk the children. Note that if either child is not
154: // an Optimizable and the call to child.getOptimizerImpl()
155: // returns null, then that means we haven't tried to optimize
156: // the child yet. So in that case there's nothing to
157: // add/load.
158:
159: if (leftResultSet instanceof Optimizable) {
160: ((Optimizable) leftResultSet).updateBestPlanMap(action,
161: planKey);
162: } else if (leftResultSet.getOptimizerImpl() != null) {
163: leftResultSet.getOptimizerImpl().updateBestPlanMaps(action,
164: planKey);
165: }
166:
167: if (rightResultSet instanceof Optimizable) {
168: ((Optimizable) rightResultSet).updateBestPlanMap(action,
169: planKey);
170: } else if (rightResultSet.getOptimizerImpl() != null) {
171: rightResultSet.getOptimizerImpl().updateBestPlanMaps(
172: action, planKey);
173: }
174: }
175:
176: /**
177: * Convert this object to a String. See comments in QueryTreeNode.java
178: * for how this should be done for tree printing.
179: *
180: * @return This object as a String
181: */
182:
183: public String toString() {
184: if (SanityManager.DEBUG) {
185: return "nestedInParens: " + false + "\n"
186: + leftResultSet.toString() + "\n"
187: + rightResultSet.toString() + "\n"
188: + super .toString();
189: } else {
190: return "";
191: }
192: }
193:
194: /**
195: * Prints the sub-nodes of this object. See QueryTreeNode.java for
196: * how tree printing is supposed to work.
197: *
198: * @param depth The depth of this node in the tree
199: */
200:
201: public void printSubNodes(int depth) {
202: if (SanityManager.DEBUG) {
203: super .printSubNodes(depth);
204:
205: if (leftResultSet != null) {
206: printLabel(depth, "leftResultSet: ");
207: leftResultSet.printSubNodes(depth + 1);
208: }
209:
210: if (rightResultSet != null) {
211: printLabel(depth, "rightResultSet: ");
212: rightResultSet.printSubNodes(depth + 1);
213: }
214: }
215: }
216:
217: /**
218: * Get the leftResultSet from this node.
219: *
220: * @return ResultSetNode The leftResultSet from this node.
221: */
222: public ResultSetNode getLeftResultSet() {
223: return leftResultSet;
224: }
225:
226: /**
227: * Get the rightResultSet from this node.
228: *
229: * @return ResultSetNode The rightResultSet from this node.
230: */
231: public ResultSetNode getRightResultSet() {
232: return rightResultSet;
233: }
234:
235: public ResultSetNode getLeftmostResultSet() {
236: if (leftResultSet instanceof TableOperatorNode) {
237: return ((TableOperatorNode) leftResultSet)
238: .getLeftmostResultSet();
239: } else {
240: return leftResultSet;
241: }
242: }
243:
244: public void setLeftmostResultSet(ResultSetNode newLeftResultSet) {
245: if (leftResultSet instanceof TableOperatorNode) {
246: ((TableOperatorNode) leftResultSet)
247: .setLeftmostResultSet(newLeftResultSet);
248: } else {
249: this .leftResultSet = newLeftResultSet;
250: }
251: }
252:
253: /**
254: * Set the (query block) level (0-based) for this FromTable.
255: *
256: * @param level The query block level for this FromTable.
257: */
258: public void setLevel(int level) {
259: super .setLevel(level);
260: if (leftResultSet instanceof FromTable) {
261: ((FromTable) leftResultSet).setLevel(level);
262: }
263: if (rightResultSet instanceof FromTable) {
264: ((FromTable) rightResultSet).setLevel(level);
265: }
266: }
267:
268: /**
269: * Return the exposed name for this table, which is the name that
270: * can be used to refer to this table in the rest of the query.
271: *
272: * @return The exposed name for this table.
273: */
274:
275: public String getExposedName() {
276: return null;
277: }
278:
279: /**
280: * Mark whether or not this node is nested in parens. (Useful to parser
281: * since some trees get created left deep and others right deep.)
282: * The resulting state of this cal was never used so its
283: * field was removed to save runtimespace for this node.
284: * Further cleanup can be done including parser changes
285: * if this call is really nor required.
286: *
287: * @param nestedInParens Whether or not this node is nested in parens.
288: */
289: public void setNestedInParens(boolean nestedInParens) {
290: }
291:
292: /**
293: * Bind the non VTI tables in this TableOperatorNode. This means getting
294: * their TableDescriptors from the DataDictionary.
295: * We will build an unbound RCL for this node. This RCL must be
296: * "bound by hand" after the underlying left and right RCLs
297: * are bound.
298: *
299: * @param dataDictionary The DataDictionary to use for binding
300: * @param fromListParam FromList to use/append to.
301: *
302: * @return ResultSetNode Returns this.
303: *
304: * @exception StandardException Thrown on error
305: */
306:
307: public ResultSetNode bindNonVTITables(
308: DataDictionary dataDictionary, FromList fromListParam)
309: throws StandardException {
310: leftResultSet = leftResultSet.bindNonVTITables(dataDictionary,
311: fromListParam);
312: rightResultSet = rightResultSet.bindNonVTITables(
313: dataDictionary, fromListParam);
314: /* Assign the tableNumber */
315: if (tableNumber == -1) // allow re-bind, in which case use old number
316: tableNumber = getCompilerContext().getNextTableNumber();
317:
318: return this ;
319: }
320:
321: /**
322: * Bind the VTI tables in this TableOperatorNode. This means getting
323: * their TableDescriptors from the DataDictionary.
324: * We will build an unbound RCL for this node. This RCL must be
325: * "bound by hand" after the underlying left and right RCLs
326: * are bound.
327: *
328: * @param fromListParam FromList to use/append to.
329: *
330: * @return ResultSetNode Returns this.
331: *
332: * @exception StandardException Thrown on error
333: */
334:
335: public ResultSetNode bindVTITables(FromList fromListParam)
336: throws StandardException {
337: leftResultSet = leftResultSet.bindVTITables(fromListParam);
338: rightResultSet = rightResultSet.bindVTITables(fromListParam);
339:
340: return this ;
341: }
342:
343: /**
344: * Bind the expressions under this TableOperatorNode. This means
345: * binding the sub-expressions, as well as figuring out what the
346: * return type is for each expression.
347: *
348: * @exception StandardException Thrown on error
349: */
350:
351: public void bindExpressions(FromList fromListParam)
352: throws StandardException {
353: /*
354: ** Parameters not allowed in select list of either side of union,
355: ** except when the union is for a table constructor.
356: */
357: if (!(this instanceof UnionNode)
358: || !((UnionNode) this ).tableConstructor()) {
359: leftResultSet.rejectParameters();
360: rightResultSet.rejectParameters();
361: }
362:
363: leftResultSet.bindExpressions(fromListParam);
364: rightResultSet.bindExpressions(fromListParam);
365: }
366:
367: /**
368: * Check for (and reject) ? parameters directly under the ResultColumns.
369: * This is done for SELECT statements. For TableOperatorNodes, we
370: * simply pass the check through to the left and right children.
371: *
372: * @exception StandardException Thrown if a ? parameter found
373: * directly under a ResultColumn
374: */
375:
376: public void rejectParameters() throws StandardException {
377: leftResultSet.rejectParameters();
378: rightResultSet.rejectParameters();
379: }
380:
381: /**
382: * Bind the expressions in this ResultSetNode if it has tables. This means binding the
383: * sub-expressions, as well as figuring out what the return type is for
384: * each expression.
385: *
386: * @param fromListParam FromList to use/append to.
387: *
388: * @exception StandardException Thrown on error
389: */
390: public void bindExpressionsWithTables(FromList fromListParam)
391: throws StandardException {
392: /*
393: ** Parameters not allowed in select list of either side of a set operator,
394: ** except when the set operator is for a table constructor.
395: */
396: if (!(this instanceof UnionNode)
397: || !((UnionNode) this ).tableConstructor()) {
398: leftResultSet.rejectParameters();
399: rightResultSet.rejectParameters();
400: }
401:
402: leftResultSet.bindExpressionsWithTables(fromListParam);
403: rightResultSet.bindExpressionsWithTables(fromListParam);
404: }
405:
406: /**
407: * Bind the result columns of this ResultSetNode when there is no
408: * base table to bind them to. This is useful for SELECT statements,
409: * where the result columns get their types from the expressions that
410: * live under them.
411: *
412: * @param fromListParam FromList to use/append to.
413: *
414: * @exception StandardException Thrown on error
415: */
416: public void bindResultColumns(FromList fromListParam)
417: throws StandardException {
418: leftResultSet.bindResultColumns(fromListParam);
419: rightResultSet.bindResultColumns(fromListParam);
420: }
421:
422: /**
423: * Bind the result columns for this ResultSetNode to a base table.
424: * This is useful for INSERT and UPDATE statements, where the
425: * result columns get their types from the table being updated or
426: * inserted into.
427: * If a result column list is specified, then the verification that the
428: * result column list does not contain any duplicates will be done when
429: * binding them by name.
430: *
431: * @param targetTableDescriptor The TableDescriptor for the table being
432: * updated or inserted into
433: * @param targetColumnList For INSERT statements, the user
434: * does not have to supply column
435: * names (for example, "insert into t
436: * values (1,2,3)". When this
437: * parameter is null, it means that
438: * the user did not supply column
439: * names, and so the binding should
440: * be done based on order. When it
441: * is not null, it means do the binding
442: * by name, not position.
443: * @param statement Calling DMLStatementNode (Insert or Update)
444: * @param fromListParam FromList to use/append to.
445: *
446: * @exception StandardException Thrown on error
447: */
448:
449: public void bindResultColumns(
450: TableDescriptor targetTableDescriptor, FromVTI targetVTI,
451: ResultColumnList targetColumnList,
452: DMLStatementNode statement, FromList fromListParam)
453: throws StandardException {
454: leftResultSet.bindResultColumns(targetTableDescriptor,
455: targetVTI, targetColumnList, statement, fromListParam);
456: rightResultSet.bindResultColumns(targetTableDescriptor,
457: targetVTI, targetColumnList, statement, fromListParam);
458: }
459:
460: /**
461: * Determine whether or not the specified name is an exposed name in
462: * the current query block.
463: *
464: * @param name The specified name to search for as an exposed name.
465: * @param schemaName Schema name, if non-null.
466: * @param exactMatch Whether or not we need an exact match on specified schema and table
467: * names or match on table id.
468: *
469: * @return The FromTable, if any, with the exposed name.
470: *
471: * @exception StandardException Thrown on error
472: */
473: protected FromTable getFromTableByName(String name,
474: String schemaName, boolean exactMatch)
475: throws StandardException {
476: FromTable result = leftResultSet.getFromTableByName(name,
477: schemaName, exactMatch);
478:
479: /* We search both sides for a TableOperatorNode (join nodes)
480: * but only the left side for a UnionNode.
481: */
482: if (result == null) {
483: result = rightResultSet.getFromTableByName(name,
484: schemaName, exactMatch);
485: }
486: return result;
487: }
488:
489: /**
490: * Put a ProjectRestrictNode on top of each FromTable in the FromList.
491: * ColumnReferences must continue to point to the same ResultColumn, so
492: * that ResultColumn must percolate up to the new PRN. However,
493: * that ResultColumn will point to a new expression, a VirtualColumnNode,
494: * which points to the FromTable and the ResultColumn that is the source for
495: * the ColumnReference.
496: * (The new PRN will have the original of the ResultColumnList and
497: * the ResultColumns from that list. The FromTable will get shallow copies
498: * of the ResultColumnList and its ResultColumns. ResultColumn.expression
499: * will remain at the FromTable, with the PRN getting a new
500: * VirtualColumnNode for each ResultColumn.expression.)
501: * We then project out the non-referenced columns. If there are no referenced
502: * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
503: * whose expression is 1.
504: *
505: * @param numTables Number of tables in the DML Statement
506: * @param gbl The group by list, if any
507: * @param fromList The from list, if any
508: *
509: * @return The generated ProjectRestrictNode atop the original FromTable.
510: *
511: * @exception StandardException Thrown on error
512: */
513:
514: public ResultSetNode preprocess(int numTables, GroupByList gbl,
515: FromList fromList) throws StandardException {
516: leftResultSet = leftResultSet.preprocess(numTables, gbl,
517: fromList);
518: /* If leftResultSet is a FromSubquery, then we must explicitly extract
519: * out the subquery (flatten it). (SelectNodes have their own
520: * method of flattening them.
521: */
522: if (leftResultSet instanceof FromSubquery) {
523: leftResultSet = ((FromSubquery) leftResultSet)
524: .extractSubquery(numTables);
525: }
526: rightResultSet = rightResultSet.preprocess(numTables, gbl,
527: fromList);
528: /* If rightResultSet is a FromSubquery, then we must explicitly extract
529: * out the subquery (flatten it). (SelectNodes have their own
530: * method of flattening them.
531: */
532: if (rightResultSet instanceof FromSubquery) {
533: rightResultSet = ((FromSubquery) rightResultSet)
534: .extractSubquery(numTables);
535: }
536:
537: /* Build the referenced table map (left || right) */
538: referencedTableMap = (JBitSet) leftResultSet
539: .getReferencedTableMap().clone();
540: referencedTableMap.or((JBitSet) rightResultSet
541: .getReferencedTableMap());
542: referencedTableMap.set(tableNumber);
543:
544: /* Only generate a PRN if this node is not a flattenable join node. */
545: if (isFlattenableJoinNode()) {
546: return this ;
547: } else {
548: /* Project out any unreferenced RCs before we generate the PRN.
549: * NOTE: We have to do this at the end of preprocess since it has to
550: * be from the bottom up. We can't do it until the join expression is
551: * bound, since the join expression may contain column references that
552: * are not referenced anywhere else above us.
553: */
554: projectResultColumns();
555: return genProjectRestrict(numTables);
556: }
557: }
558:
559: /**
560: * Find the unreferenced result columns and project them out. This is used in pre-processing joins
561: * that are not flattened into the where clause.
562: */
563: void projectResultColumns() throws StandardException {
564: resultColumns.doProjection();
565: }
566:
567: /**
568: * Set the referenced columns in the column list if it may not be correct.
569: */
570: void setReferencedColumns() {
571: }
572:
573: /**
574: * Optimize a TableOperatorNode.
575: *
576: * @param dataDictionary The DataDictionary to use for optimization
577: * @param predicateList The PredicateList to apply.
578: *
579: * @return ResultSetNode The top of the optimized query tree
580: *
581: * @exception StandardException Thrown on error
582: */
583: public ResultSetNode optimize(DataDictionary dataDictionary,
584: PredicateList predicateList, double outerRows)
585: throws StandardException {
586: /* Get an optimizer, so we can get a cost structure */
587: Optimizer optimizer = getOptimizer((FromList) getNodeFactory()
588: .getNode(C_NodeTypes.FROM_LIST,
589: getNodeFactory().doJoinOrderOptimization(),
590: this , getContextManager()), predicateList,
591: dataDictionary, (RequiredRowOrdering) null);
592:
593: costEstimate = optimizer.newCostEstimate();
594:
595: /* RESOLVE: This is just a stub for now */
596: leftResultSet = leftResultSet.optimize(dataDictionary,
597: predicateList, outerRows);
598: rightResultSet = rightResultSet.optimize(dataDictionary,
599: predicateList, outerRows);
600:
601: /* The cost is the sum of the two child costs */
602: costEstimate
603: .setCost(leftResultSet.getCostEstimate()
604: .getEstimatedCost(), leftResultSet
605: .getCostEstimate().rowCount(), leftResultSet
606: .getCostEstimate().singleScanRowCount()
607: + rightResultSet.getCostEstimate()
608: .singleScanRowCount());
609:
610: costEstimate.add(rightResultSet.costEstimate, costEstimate);
611:
612: return this ;
613: }
614:
615: /**
616: * @see ResultSetNode#modifyAccessPaths
617: *
618: * @exception StandardException Thrown on error
619: */
620: public ResultSetNode modifyAccessPaths() throws StandardException {
621: /* Beetle 4454 - union all with another union all would modify access
622: * paths twice causing NullPointerException, make sure we don't
623: * do this again, if we have already done it in modifyAccessPaths(outertables)
624: */
625: if (!leftModifyAccessPathsDone) {
626: if (leftOptimizer != null)
627: leftOptimizer.modifyAccessPaths();
628: else {
629: // If this is a SetOperatorNode then we may have pushed
630: // predicates down to the children. If that's the case
631: // then we need to pass those predicates down as part
632: // of the modifyAccessPaths call so that they can be
633: // pushed one last time, in prep for generation.
634: if (this instanceof SetOperatorNode) {
635: SetOperatorNode setOp = (SetOperatorNode) this ;
636: leftResultSet = leftResultSet
637: .modifyAccessPaths(setOp
638: .getLeftOptPredicateList());
639: } else
640: leftResultSet = leftResultSet.modifyAccessPaths();
641: }
642: }
643: if (!rightModifyAccessPathsDone) {
644: if (rightOptimizer != null)
645: rightOptimizer.modifyAccessPaths();
646: else {
647: if (this instanceof SetOperatorNode) {
648: SetOperatorNode setOp = (SetOperatorNode) this ;
649: rightResultSet = rightResultSet
650: .modifyAccessPaths(setOp
651: .getRightOptPredicateList());
652: } else
653: rightResultSet = rightResultSet.modifyAccessPaths();
654: }
655: }
656: return this ;
657: }
658:
659: /**
660: * Search to see if a query references the specifed table name.
661: *
662: * @param name Table name (String) to search for.
663: * @param baseTable Whether or not name is for a base table
664: *
665: * @return true if found, else false
666: *
667: * @exception StandardException Thrown on error
668: */
669: public boolean referencesTarget(String name, boolean baseTable)
670: throws StandardException {
671: return leftResultSet.referencesTarget(name, baseTable)
672: || rightResultSet.referencesTarget(name, baseTable);
673: }
674:
675: /**
676: * Return true if the node references SESSION schema tables (temporary or permanent)
677: *
678: * @return true if references SESSION schema tables, else false
679: *
680: * @exception StandardException Thrown on error
681: */
682: public boolean referencesSessionSchema() throws StandardException {
683: return leftResultSet.referencesSessionSchema()
684: || rightResultSet.referencesSessionSchema();
685: }
686:
687: /**
688: * Optimize a source result set to this table operator.
689: *
690: * @exception StandardException Thrown on error
691: */
692: protected ResultSetNode optimizeSource(Optimizer optimizer,
693: ResultSetNode sourceResultSet, PredicateList predList,
694: CostEstimate outerCost) throws StandardException {
695: ResultSetNode retval;
696:
697: if (sourceResultSet instanceof FromTable) {
698: FromList optList = (FromList) getNodeFactory().getNode(
699: C_NodeTypes.FROM_LIST,
700: getNodeFactory().doJoinOrderOptimization(),
701: sourceResultSet, getContextManager());
702:
703: /* If there is no predicate list, create an empty one */
704: if (predList == null)
705: predList = (PredicateList) getNodeFactory()
706: .getNode(C_NodeTypes.PREDICATE_LIST,
707: getContextManager());
708:
709: LanguageConnectionContext lcc = getLanguageConnectionContext();
710: OptimizerFactory optimizerFactory = lcc
711: .getOptimizerFactory();
712: optimizer = optimizerFactory.getOptimizer(optList,
713: predList, getDataDictionary(),
714: (RequiredRowOrdering) null, getCompilerContext()
715: .getNumTables(), lcc);
716: optimizer.prepForNextRound();
717:
718: if (sourceResultSet == leftResultSet) {
719: leftOptimizer = optimizer;
720: } else if (sourceResultSet == rightResultSet) {
721: rightOptimizer = optimizer;
722: } else {
723: if (SanityManager.DEBUG)
724: SanityManager
725: .THROWASSERT("Result set being optimized is neither left nor right");
726: }
727:
728: /*
729: ** Set the estimated number of outer rows from the outer part of
730: ** the plan.
731: */
732: optimizer.setOuterRows(outerCost.rowCount());
733:
734: /* Optimize the underlying result set */
735: while (optimizer.getNextPermutation()) {
736: while (optimizer.getNextDecoratedPermutation()) {
737: optimizer.costPermutation();
738: }
739: }
740:
741: retval = sourceResultSet;
742: } else {
743: retval = sourceResultSet.optimize(optimizer
744: .getDataDictionary(), predList, outerCost
745: .rowCount());
746: }
747:
748: return retval;
749: }
750:
751: /**
752: * Decrement (query block) level (0-based) for
753: * all of the tables in this ResultSet tree.
754: * This is useful when flattening a subquery.
755: *
756: * @param decrement The amount to decrement by.
757: */
758: void decrementLevel(int decrement) {
759: leftResultSet.decrementLevel(decrement);
760: rightResultSet.decrementLevel(decrement);
761: }
762:
763: /**
764: * Replace any DEFAULTs with the associated tree for the default.
765: *
766: * @param ttd The TableDescriptor for the target table.
767: * @param tcl The RCL for the target table.
768: *
769: * @exception StandardException Thrown on error
770: */
771: void replaceDefaults(TableDescriptor ttd, ResultColumnList tcl)
772: throws StandardException {
773: leftResultSet.replaceDefaults(ttd, tcl);
774: rightResultSet.replaceDefaults(ttd, tcl);
775: }
776:
777: /**
778: * Notify the underlying result set tree that the result is
779: * ordering dependent. (For example, no bulk fetch on an index
780: * if under an IndexRowToBaseRow.)
781: */
782: void markOrderingDependent() {
783: leftResultSet.markOrderingDependent();
784: rightResultSet.markOrderingDependent();
785: }
786:
787: /**
788: * Accept a visitor, and call v.visit()
789: * on child nodes as necessary.
790: *
791: * @param v the visitor
792: *
793: * @exception StandardException on error
794: */
795: public Visitable accept(Visitor v) throws StandardException {
796: if (v.skipChildren(this )) {
797: return v.visit(this );
798: }
799:
800: Visitable returnNode = super .accept(v);
801:
802: if (leftResultSet != null && !v.stopTraversal()) {
803: leftResultSet = (ResultSetNode) leftResultSet.accept(v);
804: }
805: if (rightResultSet != null && !v.stopTraversal()) {
806: rightResultSet = (ResultSetNode) rightResultSet.accept(v);
807: }
808: return returnNode;
809: }
810:
811: /**
812: * apparently something special needs to be done for me....
813: */
814: public boolean needsSpecialRCLBinding() {
815: return true;
816: }
817: }
|