0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.sql.compile.ProjectRestrictNode
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.sql.compile;
0023:
0024: import org.apache.derby.iapi.services.context.ContextManager;
0025:
0026: import org.apache.derby.iapi.sql.compile.Optimizable;
0027: import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
0028: import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
0029: import org.apache.derby.iapi.sql.compile.Optimizer;
0030: import org.apache.derby.iapi.sql.compile.CostEstimate;
0031: import org.apache.derby.iapi.sql.compile.OptimizableList;
0032: import org.apache.derby.iapi.sql.compile.Visitable;
0033: import org.apache.derby.iapi.sql.compile.Visitor;
0034: import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
0035: import org.apache.derby.iapi.sql.compile.RowOrdering;
0036: import org.apache.derby.iapi.sql.compile.AccessPath;
0037: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
0038:
0039: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
0040: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
0041:
0042: import org.apache.derby.iapi.types.DataValueDescriptor;
0043:
0044: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
0045:
0046: import org.apache.derby.iapi.sql.Activation;
0047: import org.apache.derby.iapi.sql.ResultSet;
0048:
0049: import org.apache.derby.iapi.error.StandardException;
0050: import org.apache.derby.iapi.reference.ClassName;
0051:
0052: import org.apache.derby.iapi.store.access.TransactionController;
0053:
0054: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
0055: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
0056:
0057: import org.apache.derby.iapi.services.compiler.MethodBuilder;
0058:
0059: import org.apache.derby.iapi.services.loader.GeneratedMethod;
0060:
0061: import org.apache.derby.iapi.services.sanity.SanityManager;
0062:
0063: import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
0064: import org.apache.derby.iapi.util.JBitSet;
0065: import org.apache.derby.iapi.services.classfile.VMOpcode;
0066:
0067: import java.util.Properties;
0068: import java.util.HashSet;
0069: import java.util.Set;
0070:
0071: /**
0072: * A ProjectRestrictNode represents a result set for any of the basic DML
0073: * operations: SELECT, INSERT, UPDATE, and DELETE. For INSERT with
0074: * a VALUES clause, restriction will be null. For both INSERT and UPDATE,
0075: * the resultColumns in the selectList will contain the names of the columns
0076: * being inserted into or updated.
0077: *
0078: * NOTE: A ProjectRestrictNode extends FromTable since it can exist in a FromList.
0079: *
0080: * @author Jeff Lichtman
0081: */
0082:
0083: public class ProjectRestrictNode extends SingleChildResultSetNode {
0084: /**
0085: * The ValueNode for the restriction to be evaluated here.
0086: */
0087: public ValueNode restriction;
0088:
0089: /**
0090: * Constant expressions to be evaluated here.
0091: */
0092: ValueNode constantRestriction = null;
0093:
0094: /**
0095: * Restriction as a PredicateList
0096: */
0097: public PredicateList restrictionList;
0098:
0099: /**
0100: * List of subqueries in projection
0101: */
0102: SubqueryList projectSubquerys;
0103:
0104: /**
0105: * List of subqueries in restriction
0106: */
0107: SubqueryList restrictSubquerys;
0108:
0109: private boolean accessPathModified;
0110:
0111: private boolean accessPathConsidered;
0112:
0113: private boolean childResultOptimized;
0114:
0115: private boolean materialize;
0116:
0117: /* Should we get the table number from this node,
0118: * regardless of the class of our child.
0119: */
0120: private boolean getTableNumberHere;
0121:
0122: /**
0123: * Initializer for a ProjectRestrictNode.
0124: *
0125: * @param childResult The child ResultSetNode
0126: * @param projection The result column list for the projection
0127: * @param restriction An expression representing the restriction to be
0128: * evaluated here.
0129: * @param restrictionList Restriction as a PredicateList
0130: * @param projectSubquerys List of subqueries in the projection
0131: * @param restrictSubquerys List of subqueries in the restriction
0132: * @param tableProperties Properties list associated with the table
0133: */
0134:
0135: public void init(Object childResult, Object projection,
0136: Object restriction, Object restrictionList,
0137: Object projectSubquerys, Object restrictSubquerys,
0138: Object tableProperties) {
0139: super .init(childResult, tableProperties);
0140: resultColumns = (ResultColumnList) projection;
0141: this .restriction = (ValueNode) restriction;
0142: this .restrictionList = (PredicateList) restrictionList;
0143: this .projectSubquerys = (SubqueryList) projectSubquerys;
0144: this .restrictSubquerys = (SubqueryList) restrictSubquerys;
0145:
0146: /* A PRN will only hold the tableProperties for
0147: * a result set tree if its child is not an
0148: * optimizable. Otherwise, the properties will
0149: * be transferred down to the child.
0150: */
0151: if (tableProperties != null
0152: && (childResult instanceof Optimizable)) {
0153: ((Optimizable) childResult).setProperties(getProperties());
0154: setProperties((Properties) null);
0155: }
0156: }
0157:
0158: /*
0159: * Optimizable interface
0160: */
0161:
0162: /**
0163: @see Optimizable#nextAccessPath
0164: @exception StandardException Thrown on error
0165: */
0166: public boolean nextAccessPath(Optimizer optimizer,
0167: OptimizablePredicateList predList, RowOrdering rowOrdering)
0168: throws StandardException {
0169: /*
0170: ** If the child result set is an optimizable, let it choose its next
0171: ** access path. If it is not an optimizable, we have to tell the
0172: ** caller that there is an access path the first time we are called
0173: ** for this position in the join order, and that there are no more
0174: ** access paths for subsequent calls for this position in the join
0175: ** order. The startOptimizing() method is called once on each
0176: ** optimizable when it is put into a join position.
0177: */
0178: if (childResult instanceof Optimizable) {
0179: return ((Optimizable) childResult).nextAccessPath(
0180: optimizer, restrictionList, rowOrdering);
0181: } else {
0182: return super .nextAccessPath(optimizer, predList,
0183: rowOrdering);
0184: }
0185: }
0186:
0187: /** @see Optimizable#rememberAsBest
0188: @exception StandardException Thrown on error
0189: */
0190: public void rememberAsBest(int planType, Optimizer optimizer)
0191: throws StandardException {
0192: super .rememberAsBest(planType, optimizer);
0193: if (childResult instanceof Optimizable)
0194: ((Optimizable) childResult).rememberAsBest(planType,
0195: optimizer);
0196: }
0197:
0198: /* Don't print anything for a PRN, as their
0199: * child has the interesting info.
0200: */
0201: void printRememberingBestAccessPath(int planType,
0202: AccessPath bestPath) {
0203: }
0204:
0205: /** @see Optimizable#startOptimizing */
0206: public void startOptimizing(Optimizer optimizer,
0207: RowOrdering rowOrdering) {
0208: if (childResult instanceof Optimizable) {
0209: ((Optimizable) childResult).startOptimizing(optimizer,
0210: rowOrdering);
0211: } else {
0212: accessPathConsidered = false;
0213:
0214: super .startOptimizing(optimizer, rowOrdering);
0215: }
0216: }
0217:
0218: /** @see Optimizable#getTableNumber */
0219: public int getTableNumber() {
0220: /* GROSS HACK - We need to get the tableNumber after
0221: * calling modifyAccessPaths() on the child when doing
0222: * a hash join on an arbitrary result set. The problem
0223: * is that the child will always be an optimizable at this
0224: * point. So, we 1st check to see if we should get it from
0225: * this node. (We set the boolean to true in the appropriate
0226: * place in modifyAccessPaths().)
0227: */
0228: if (getTableNumberHere) {
0229: return super .getTableNumber();
0230: }
0231:
0232: if (childResult instanceof Optimizable)
0233: return ((Optimizable) childResult).getTableNumber();
0234:
0235: return super .getTableNumber();
0236: }
0237:
0238: /**
0239: * @see Optimizable#optimizeIt
0240: *
0241: * @exception StandardException Thrown on error
0242: */
0243: public CostEstimate optimizeIt(Optimizer optimizer,
0244: OptimizablePredicateList predList, CostEstimate outerCost,
0245: RowOrdering rowOrdering) throws StandardException {
0246: /*
0247: ** RESOLVE: Most types of Optimizables only implement estimateCost(),
0248: ** and leave it up to optimizeIt() in FromTable to figure out the
0249: ** total cost of the join. A ProjectRestrict can have a non-Optimizable
0250: ** child, though, in which case we want to tell the child the
0251: ** number of outer rows - it could affect the join strategy
0252: ** significantly. So we implement optimizeIt() here, which overrides
0253: ** the optimizeIt() in FromTable. This assumes that the join strategy
0254: ** for which this join node is the inner table is a nested loop join,
0255: ** which will not be a valid assumption when we implement other
0256: ** strategies like materialization (hash join can work only on
0257: ** base tables). The join strategy for a base table under a
0258: ** ProjectRestrict is set in the base table itself.
0259: */
0260:
0261: CostEstimate childCost;
0262:
0263: costEstimate = getCostEstimate(optimizer);
0264:
0265: /*
0266: ** Don't re-optimize a child result set that has already been fully
0267: ** optimized. For example, if the child result set is a SelectNode,
0268: ** it will be changed to a ProjectRestrictNode, which we don't want
0269: ** to re-optimized.
0270: */
0271: // NOTE: TO GET THE RIGHT COST, THE CHILD RESULT MAY HAVE TO BE
0272: // OPTIMIZED MORE THAN ONCE, BECAUSE THE NUMBER OF OUTER ROWS
0273: // MAY BE DIFFERENT EACH TIME.
0274: // if (childResultOptimized)
0275: // return costEstimate;
0276: // It's possible that a call to optimize the left/right will cause
0277: // a new "truly the best" plan to be stored in the underlying base
0278: // tables. If that happens and then we decide to skip that plan
0279: // (which we might do if the call to "considerCost()" below decides
0280: // the current path is infeasible or not the best) we need to be
0281: // able to revert back to the "truly the best" plans that we had
0282: // saved before we got here. So with this next call we save the
0283: // current plans using "this" node as the key. If needed, we'll
0284: // then make the call to revert the plans in OptimizerImpl's
0285: // getNextDecoratedPermutation() method.
0286: updateBestPlanMap(ADD_PLAN, this );
0287:
0288: /* If the childResult is instanceof Optimizable, then we optimizeIt.
0289: * Otherwise, we are going into a new query block. If the new query
0290: * block has already had its access path modified, then there is
0291: * nothing to do. Otherwise, we must begin the optimization process
0292: * anew on the new query block.
0293: */
0294: if (childResult instanceof Optimizable) {
0295: childCost = ((Optimizable) childResult).optimizeIt(
0296: optimizer, restrictionList, outerCost, rowOrdering);
0297: /* Copy child cost to this node's cost */
0298: costEstimate.setCost(childCost.getEstimatedCost(),
0299: childCost.rowCount(), childCost
0300: .singleScanRowCount());
0301:
0302: // Note: we don't call "optimizer.considerCost()" here because
0303: // a) the child will make that call as part of its own
0304: // "optimizeIt()" work above, and b) the child might have
0305: // different criteria for "considering" (i.e. rejecting or
0306: // accepting) a plan's cost than this ProjectRestrictNode does--
0307: // and we don't want to override the child's decision. So as
0308: // with most operations in this class, if the child is an
0309: // Optimizable, we just let it do its own work and make its
0310: // own decisions.
0311: } else if (!accessPathModified) {
0312: if (SanityManager.DEBUG) {
0313: if (!((childResult instanceof SelectNode) || (childResult instanceof RowResultSetNode))) {
0314: SanityManager
0315: .THROWASSERT("childResult is expected to be instanceof "
0316: + "SelectNode or RowResultSetNode - it is a "
0317: + childResult.getClass().getName());
0318: }
0319: }
0320: childResult = childResult.optimize(optimizer
0321: .getDataDictionary(), restrictionList, outerCost
0322: .rowCount());
0323:
0324: /* Copy child cost to this node's cost */
0325: childCost = childResult.costEstimate;
0326:
0327: costEstimate.setCost(childCost.getEstimatedCost(),
0328: childCost.rowCount(), childCost
0329: .singleScanRowCount());
0330:
0331: /* Note: Prior to the fix for DERBY-781 we had calls here
0332: * to set the cost estimate for BestAccessPath and
0333: * BestSortAvoidancePath to equal costEstimate. That used
0334: * to be okay because prior to DERBY-781 we would only
0335: * get here once (per join order) for a given SelectNode/
0336: * RowResultSetNode and thus we could safely say that the
0337: * costEstimate from the most recent call to "optimize()"
0338: * was the best one so far (because we knew that we would
0339: * only call childResult.optimize() once). Now that we
0340: * support hash joins with subqueries, though, we can get
0341: * here twice per join order: once when the optimizer is
0342: * considering a nested loop join with this PRN, and once
0343: * when it is considering a hash join. This means we can't
0344: * just arbitrarily use the cost estimate for the most recent
0345: * "optimize()" as the best cost because that may not
0346: * be accurate--it's possible that the above call to
0347: * childResult.optimize() was for a hash join, but that
0348: * we were here once before (namely for nested loop) and
0349: * the cost of the nested loop is actually less than
0350: * the cost of the hash join. In that case it would
0351: * be wrong to use costEstimate as the cost of the "best"
0352: * paths because it (costEstimate) holds the cost of
0353: * the hash join, not of the nested loop join. So with
0354: * DERBY-781 the following calls were removed:
0355: * getBestAccessPath().setCostEstimate(costEstimate);
0356: * getBestSortAvoidancePath().setCostEstimate(costEstimate);
0357: * If costEstimate *does* actually hold the estimate for
0358: * the best path so far, then we will set BestAccessPath
0359: * and BestSortAvoidancePath as needed in the following
0360: * call to "considerCost".
0361: */
0362:
0363: // childResultOptimized = true;
0364: /* RESOLVE - ARBITRARYHASHJOIN - Passing restriction list here, as above, is correct.
0365: * However, passing predList makes the following work:
0366: * select * from t1, (select * from t2) c properties joinStrategy = hash where t1.c1 = c.c1;
0367: * The following works with restrictionList:
0368: * select * from t1, (select c1 + 0 from t2) c(c1) properties joinStrategy = hash where t1.c1 = c.c1;
0369: */
0370: optimizer.considerCost(this , restrictionList,
0371: getCostEstimate(), outerCost);
0372: }
0373:
0374: return costEstimate;
0375: }
0376:
0377: /**
0378: * @see Optimizable#feasibleJoinStrategy
0379: *
0380: * @exception StandardException Thrown on error
0381: */
0382: public boolean feasibleJoinStrategy(
0383: OptimizablePredicateList predList, Optimizer optimizer)
0384: throws StandardException {
0385: AccessPath ap;
0386:
0387: /* The child being an Optimizable is a special case. In that
0388: * case, we want to get the current access path and join strategy
0389: * from the child. Otherwise, we want to get it from this node.
0390: */
0391: if (childResult instanceof Optimizable) {
0392: // With DERBY-805 it's possible that, when considering a nested
0393: // loop join with this PRN, we pushed predicates down into the
0394: // child if the child is a UNION node. At this point, though, we
0395: // may be considering doing a hash join with this PRN instead of a
0396: // nested loop join, and if that's the case we need to pull any
0397: // predicates back up so that they can be searched for equijoins
0398: // that will in turn make the hash join possible. So that's what
0399: // the next call does. Two things to note: 1) if no predicates
0400: // were pushed, this call is a no-op; and 2) if we get here when
0401: // considering a nested loop join, the predicates that we pull
0402: // here (if any) will be re-pushed for subsequent costing/
0403: // optimization as necessary (see OptimizerImpl.costPermutation(),
0404: // which will call this class's optimizeIt() method and that's
0405: // where the predicates are pushed down again).
0406: if (childResult instanceof UnionNode)
0407: ((UnionNode) childResult)
0408: .pullOptPredicates(restrictionList);
0409:
0410: return ((Optimizable) childResult).feasibleJoinStrategy(
0411: restrictionList, optimizer);
0412: } else {
0413: return super .feasibleJoinStrategy(restrictionList,
0414: optimizer);
0415: }
0416: }
0417:
0418: /** @see Optimizable#getCurrentAccessPath */
0419: public AccessPath getCurrentAccessPath() {
0420: if (childResult instanceof Optimizable)
0421: return ((Optimizable) childResult).getCurrentAccessPath();
0422:
0423: return super .getCurrentAccessPath();
0424: }
0425:
0426: /** @see Optimizable#getBestAccessPath */
0427: public AccessPath getBestAccessPath() {
0428: if (childResult instanceof Optimizable)
0429: return ((Optimizable) childResult).getBestAccessPath();
0430:
0431: return super .getBestAccessPath();
0432: }
0433:
0434: /** @see Optimizable#getBestSortAvoidancePath */
0435: public AccessPath getBestSortAvoidancePath() {
0436: if (childResult instanceof Optimizable)
0437: return ((Optimizable) childResult)
0438: .getBestSortAvoidancePath();
0439:
0440: return super .getBestSortAvoidancePath();
0441: }
0442:
0443: /** @see Optimizable#getTrulyTheBestAccessPath */
0444: public AccessPath getTrulyTheBestAccessPath() {
0445: /* The childResult will always be an Optimizable
0446: * during code generation. If the childResult was
0447: * not an Optimizable during optimization, then this node
0448: * will have the truly the best access path, so we want to
0449: * return it from this node, rather than traversing the tree.
0450: * This can happen for non-flattenable derived tables.
0451: * Anyway, we note this state when modifying the access paths.
0452: */
0453: if (hasTrulyTheBestAccessPath) {
0454: return super .getTrulyTheBestAccessPath();
0455: }
0456:
0457: if (childResult instanceof Optimizable)
0458: return ((Optimizable) childResult)
0459: .getTrulyTheBestAccessPath();
0460:
0461: return super .getTrulyTheBestAccessPath();
0462: }
0463:
0464: /** @see Optimizable#rememberSortAvoidancePath */
0465: public void rememberSortAvoidancePath() {
0466: if (childResult instanceof Optimizable)
0467: ((Optimizable) childResult).rememberSortAvoidancePath();
0468: else
0469: super .rememberSortAvoidancePath();
0470: }
0471:
0472: /** @see Optimizable#considerSortAvoidancePath */
0473: public boolean considerSortAvoidancePath() {
0474: if (childResult instanceof Optimizable)
0475: return ((Optimizable) childResult)
0476: .considerSortAvoidancePath();
0477:
0478: return super .considerSortAvoidancePath();
0479: }
0480:
0481: /**
0482: * @see Optimizable#pushOptPredicate
0483: *
0484: * @exception StandardException Thrown on error
0485: */
0486:
0487: public boolean pushOptPredicate(
0488: OptimizablePredicate optimizablePredicate)
0489: throws StandardException {
0490: if (SanityManager.DEBUG) {
0491: SanityManager
0492: .ASSERT(optimizablePredicate instanceof Predicate,
0493: "optimizablePredicate expected to be instanceof Predicate");
0494: SanityManager
0495: .ASSERT(!optimizablePredicate.hasSubquery()
0496: && !optimizablePredicate.hasMethodCall(),
0497: "optimizablePredicate either has a subquery or a method call");
0498: }
0499:
0500: /* Add the matching predicate to the restrictionList */
0501: if (restrictionList == null) {
0502: restrictionList = (PredicateList) getNodeFactory().getNode(
0503: C_NodeTypes.PREDICATE_LIST, getContextManager());
0504: }
0505: restrictionList.addPredicate((Predicate) optimizablePredicate);
0506:
0507: /* Remap all of the ColumnReferences to point to the
0508: * source of the values.
0509: */
0510: Predicate pred = (Predicate) optimizablePredicate;
0511:
0512: /* If the predicate is scoped then the call to "remapScopedPred()"
0513: * will do the necessary remapping for us and will return true;
0514: * otherwise, we'll just do the normal remapping here.
0515: */
0516: if (!pred.remapScopedPred()) {
0517: RemapCRsVisitor rcrv = new RemapCRsVisitor(true);
0518: pred.getAndNode().accept(rcrv);
0519: }
0520:
0521: return true;
0522: }
0523:
0524: /**
0525: * @see Optimizable#pullOptPredicates
0526: *
0527: * @exception StandardException Thrown on error
0528: */
0529: public void pullOptPredicates(
0530: OptimizablePredicateList optimizablePredicates)
0531: throws StandardException {
0532: if (restrictionList != null) {
0533: // Pull up any predicates that may have been pushed further
0534: // down the tree during optimization.
0535: if (childResult instanceof UnionNode)
0536: ((UnionNode) childResult)
0537: .pullOptPredicates(restrictionList);
0538:
0539: RemapCRsVisitor rcrv = new RemapCRsVisitor(false);
0540: for (int i = restrictionList.size() - 1; i >= 0; i--) {
0541: OptimizablePredicate optPred = restrictionList
0542: .getOptPredicate(i);
0543: ((Predicate) optPred).getAndNode().accept(rcrv);
0544: optimizablePredicates.addOptPredicate(optPred);
0545: restrictionList.removeOptPredicate(i);
0546: }
0547: }
0548: }
0549:
0550: /**
0551: * @see Optimizable#modifyAccessPath
0552: *
0553: * @exception StandardException Thrown on error
0554: */
0555: public Optimizable modifyAccessPath(JBitSet outerTables)
0556: throws StandardException {
0557: boolean origChildOptimizable = true;
0558:
0559: /* It is okay to optimize most nodes multiple times. However,
0560: * modifying the access path is something that should only be done
0561: * once per node. One reason for this is that the predicate list
0562: * will be empty after the 1st call, and we assert that it should
0563: * be non-empty. Multiple calls to modify the access path can
0564: * occur when there is a non-flattenable FromSubquery (or view).
0565: */
0566: if (accessPathModified) {
0567: return this ;
0568: }
0569:
0570: /*
0571: ** Do nothing if the child result set is not optimizable, as there
0572: ** can be nothing to modify.
0573: */
0574: boolean alreadyPushed = false;
0575: if (!(childResult instanceof Optimizable)) {
0576: // Remember that the original child was not Optimizable
0577: origChildOptimizable = false;
0578:
0579: /* When we optimized the child we passed in our restriction list
0580: * so that scoped predicates could be pushed further down the
0581: * tree. We need to do the same when modifying the access
0582: * paths to ensure we generate the same plans the optimizer
0583: * chose.
0584: */
0585: childResult = childResult
0586: .modifyAccessPaths(restrictionList);
0587:
0588: /* Mark this node as having the truly ... for
0589: * the underlying tree.
0590: */
0591: hasTrulyTheBestAccessPath = true;
0592:
0593: /* Replace this PRN with a HRN if we are doing a hash join */
0594: if (trulyTheBestAccessPath.getJoinStrategy().isHashJoin()) {
0595: if (SanityManager.DEBUG) {
0596: SanityManager.ASSERT(restrictionList != null,
0597: "restrictionList expected to be non-null");
0598: SanityManager
0599: .ASSERT(restrictionList.size() != 0,
0600: "restrictionList.size() expected to be non-zero");
0601: }
0602: /* We're doing a hash join on an arbitary result set.
0603: * We need to get the table number from this node when
0604: * dividing up the restriction list for a hash join.
0605: * We need to explicitly remember this.
0606: */
0607: getTableNumberHere = true;
0608: } else {
0609: /* We consider materialization into a temp table as a last step.
0610: * Currently, we only materialize VTIs that are inner tables
0611: * and can't be instantiated multiple times. In the future we
0612: * will consider materialization as a cost based option.
0613: */
0614: return (Optimizable) considerMaterialization(outerTables);
0615: }
0616: }
0617:
0618: /* If the child is not a FromBaseTable, then we want to
0619: * keep going down the tree. (Nothing to do at this node.)
0620: */
0621: else if (!(childResult instanceof FromBaseTable)) {
0622: /* Make sure that we have a join strategy */
0623: if (trulyTheBestAccessPath.getJoinStrategy() == null) {
0624: trulyTheBestAccessPath = (AccessPathImpl) ((Optimizable) childResult)
0625: .getTrulyTheBestAccessPath();
0626: }
0627:
0628: // If the childResult is a SetOperatorNode (esp. a UnionNode),
0629: // then it's possible that predicates in our restrictionList are
0630: // supposed to be pushed further down the tree (as of DERBY-805).
0631: // We passed the restrictionList down when we optimized the child
0632: // so that the relevant predicates could be pushed further as part
0633: // of the optimization process; so now that we're finalizing the
0634: // paths, we need to do the same thing: i.e. pass restrictionList
0635: // down so that the predicates that need to be pushed further
0636: // _can_ be pushed further.
0637: if (childResult instanceof SetOperatorNode) {
0638: childResult = (ResultSetNode) ((SetOperatorNode) childResult)
0639: .modifyAccessPath(outerTables, restrictionList);
0640:
0641: // Take note of the fact that we already pushed predicates
0642: // as part of the modifyAccessPaths call. This is necessary
0643: // because there may still be predicates in restrictionList
0644: // that we intentionally decided not to push (ex. if we're
0645: // going to do hash join then we chose to not push the join
0646: // predicates). Whatever the reason for not pushing the
0647: // predicates, we have to make sure we don't inadvertenly
0648: // push them later (esp. as part of the "pushUsefulPredicates"
0649: // call below).
0650: alreadyPushed = true;
0651: } else {
0652: childResult = (ResultSetNode) ((FromTable) childResult)
0653: .modifyAccessPath(outerTables);
0654: }
0655: }
0656:
0657: // If we're doing a hash join with _this_ PRN (as opposed to
0658: // with this PRN's child) then we don't attempt to push
0659: // predicates down. There are two reasons for this: 1)
0660: // we don't want to push the equijoin predicate that is
0661: // required for the hash join, and 2) if we're doing a
0662: // hash join then we're going to materialize this node,
0663: // but if we push predicates before materialization, we
0664: // can end up with incorrect results (esp. missing rows).
0665: // So don't push anything in this case.
0666: boolean hashJoinWithThisPRN = hasTrulyTheBestAccessPath
0667: && (trulyTheBestAccessPath.getJoinStrategy() != null)
0668: && trulyTheBestAccessPath.getJoinStrategy()
0669: .isHashJoin();
0670:
0671: if ((restrictionList != null) && !alreadyPushed
0672: && !hashJoinWithThisPRN) {
0673: restrictionList
0674: .pushUsefulPredicates((Optimizable) childResult);
0675: }
0676:
0677: /*
0678: ** The optimizer's decision on the access path for the child result
0679: ** set may require the generation of extra result sets. For
0680: ** example, if it chooses an index, we need an IndexToBaseRowNode
0681: ** above the FromBaseTable (and the FromBaseTable has to change
0682: ** its column list to match that of the index.
0683: */
0684: if (origChildOptimizable) {
0685: childResult = childResult.changeAccessPath();
0686: }
0687: accessPathModified = true;
0688:
0689: /*
0690: ** Replace this PRN with a HTN if a hash join
0691: ** is being done at this node. (Hash join on a scan
0692: ** is a special case and is handled at the FBT.)
0693: */
0694: if (trulyTheBestAccessPath.getJoinStrategy() != null
0695: && trulyTheBestAccessPath.getJoinStrategy()
0696: .isHashJoin()) {
0697: return replaceWithHashTableNode();
0698: }
0699:
0700: /* We consider materialization into a temp table as a last step.
0701: * Currently, we only materialize VTIs that are inner tables
0702: * and can't be instantiated multiple times. In the future we
0703: * will consider materialization as a cost based option.
0704: */
0705: return (Optimizable) considerMaterialization(outerTables);
0706: }
0707:
0708: /**
0709: * This method creates a HashTableNode between the PRN and
0710: * it's child when the optimizer chooses hash join on an
0711: * arbitrary (non-FBT) result set tree.
0712: * We divide up the restriction list into 3 parts and
0713: * distribute those parts as described below.
0714: *
0715: * @return The new (same) top of our result set tree.
0716: * @exception StandardException Thrown on error
0717: */
0718: private Optimizable replaceWithHashTableNode()
0719: throws StandardException {
0720: // If this PRN has TTB access path for its child, store that access
0721: // path in the child here, so that we can find it later when it
0722: // comes time to generate qualifiers for the hash predicates (we
0723: // need the child's access path when generating qualifiers; if we
0724: // don't pass the path down here, the child won't be able to find
0725: // it).
0726: if (hasTrulyTheBestAccessPath) {
0727: ((FromTable) childResult).trulyTheBestAccessPath = (AccessPathImpl) getTrulyTheBestAccessPath();
0728:
0729: // If the child itself is another SingleChildResultSetNode
0730: // (which is also what a ProjectRestrictNode is), then tell
0731: // it that it is now holding TTB path for it's own child. Again,
0732: // this info is needed so that child knows where to find the
0733: // access path at generation time.
0734: if (childResult instanceof SingleChildResultSetNode) {
0735: ((SingleChildResultSetNode) childResult).hasTrulyTheBestAccessPath = hasTrulyTheBestAccessPath;
0736:
0737: // While we're at it, add the PRN's table number to the
0738: // child's referenced map so that we can find the equijoin
0739: // predicate. We have to do this because the predicate
0740: // will be referencing the PRN's tableNumber, not the
0741: // child's--and since we use the child as the target
0742: // when searching for hash keys (as can be seen in
0743: // HashJoinStrategy.divideUpPredicateLists()), the child
0744: // should know what this PRN's table number is. This
0745: // is somewhat bizarre since the child doesn't
0746: // actually "reference" this PRN, but since the child's
0747: // reference map is used when searching for the equijoin
0748: // predicate (see "buildTableNumList" in
0749: // BinaryRelationalOperatorNode), this is the simplest
0750: // way to pass this PRN's table number down.
0751: childResult.getReferencedTableMap().set(tableNumber);
0752: }
0753: }
0754:
0755: /* We want to divide the predicate list into 3 separate lists -
0756: * o predicates against the source of the hash table, which will
0757: * be applied on the way into the hash table (searchRestrictionList)
0758: * o join clauses which are qualifiers and get applied to the
0759: * rows in the hash table on a probe (joinRestrictionList)
0760: * o non-qualifiers involving both tables which will get
0761: * applied after a row gets returned from the HTRS (nonQualifiers)
0762: *
0763: * We do some unnecessary work when doing this as we want to reuse
0764: * as much existing code as possible. The code that we are reusing
0765: * was originally built for hash scans, hence the unnecessary
0766: * requalification list.
0767: */
0768: PredicateList searchRestrictionList = (PredicateList) getNodeFactory()
0769: .getNode(C_NodeTypes.PREDICATE_LIST,
0770: getContextManager());
0771: PredicateList joinQualifierList = (PredicateList) getNodeFactory()
0772: .getNode(C_NodeTypes.PREDICATE_LIST,
0773: getContextManager());
0774: PredicateList requalificationRestrictionList = (PredicateList) getNodeFactory()
0775: .getNode(C_NodeTypes.PREDICATE_LIST,
0776: getContextManager());
0777: trulyTheBestAccessPath.getJoinStrategy()
0778: .divideUpPredicateLists(this , restrictionList,
0779: searchRestrictionList, joinQualifierList,
0780: requalificationRestrictionList,
0781: getDataDictionary());
0782:
0783: /* Break out the non-qualifiers from HTN's join qualifier list and make that
0784: * the new restriction list for this PRN.
0785: */
0786: restrictionList = (PredicateList) getNodeFactory().getNode(
0787: C_NodeTypes.PREDICATE_LIST, getContextManager());
0788: /* For non-base table, we remove first 2 lists from requal list to avoid adding duplicates.
0789: */
0790: for (int i = 0; i < searchRestrictionList.size(); i++)
0791: requalificationRestrictionList
0792: .removeOptPredicate((Predicate) searchRestrictionList
0793: .elementAt(i));
0794: for (int i = 0; i < joinQualifierList.size(); i++)
0795: requalificationRestrictionList
0796: .removeOptPredicate((Predicate) joinQualifierList
0797: .elementAt(i));
0798:
0799: joinQualifierList.transferNonQualifiers(this , restrictionList); //purify joinQual list
0800: requalificationRestrictionList
0801: .copyPredicatesToOtherList(restrictionList); //any residual
0802:
0803: ResultColumnList htRCList;
0804:
0805: /* We get a shallow copy of the child's ResultColumnList and its
0806: * ResultColumns. (Copy maintains ResultColumn.expression for now.)
0807: */
0808: htRCList = childResult.getResultColumns();
0809: childResult.setResultColumns(htRCList.copyListAndObjects());
0810:
0811: /* Replace ResultColumn.expression with new VirtualColumnNodes
0812: * in the HTN's ResultColumnList. (VirtualColumnNodes include
0813: * pointers to source ResultSetNode, this, and source ResultColumn.)
0814: * NOTE: We don't want to mark the underlying RCs as referenced, otherwise
0815: * we won't be able to project out any of them.
0816: */
0817: htRCList.genVirtualColumnNodes(childResult, childResult
0818: .getResultColumns(), false);
0819:
0820: /* The CRs for this side of the join in both the searchRestrictionList
0821: * the joinQualifierList now point to the HTN's RCL. We need them
0822: * to point to the RCL in the child of the HTN. (We skip doing this for
0823: * the joinQualifierList as the code to generate the Qualifiers does not
0824: * care.)
0825: */
0826: RemapCRsVisitor rcrv = new RemapCRsVisitor(true);
0827: searchRestrictionList.accept(rcrv);
0828:
0829: /* We can finally put the HTN between ourself and our old child. */
0830: childResult = (ResultSetNode) getNodeFactory().getNode(
0831: C_NodeTypes.HASH_TABLE_NODE, childResult,
0832: tableProperties, htRCList, searchRestrictionList,
0833: joinQualifierList, trulyTheBestAccessPath,
0834: getCostEstimate(), projectSubquerys, restrictSubquerys,
0835: hashKeyColumns(), getContextManager());
0836: return this ;
0837: }
0838:
0839: /** @see Optimizable#verifyProperties
0840: * @exception StandardException Thrown on error
0841: */
0842: public void verifyProperties(DataDictionary dDictionary)
0843: throws StandardException {
0844: /* Table properties can be attached to this node if
0845: * its child is not an optimizable, otherwise they
0846: * are attached to its child.
0847: */
0848:
0849: if (childResult instanceof Optimizable) {
0850: ((Optimizable) childResult).verifyProperties(dDictionary);
0851: } else {
0852: super .verifyProperties(dDictionary);
0853: }
0854: }
0855:
0856: /**
0857: * @see Optimizable#legalJoinOrder
0858: */
0859: public boolean legalJoinOrder(JBitSet assignedTableMap) {
0860: if (childResult instanceof Optimizable) {
0861: return ((Optimizable) childResult)
0862: .legalJoinOrder(assignedTableMap);
0863: } else {
0864: return true;
0865: }
0866: }
0867:
0868: /**
0869: * @see Optimizable#uniqueJoin
0870: *
0871: * @exception StandardException Thrown on error
0872: */
0873: public double uniqueJoin(OptimizablePredicateList predList)
0874: throws StandardException {
0875: if (childResult instanceof Optimizable) {
0876: return ((Optimizable) childResult).uniqueJoin(predList);
0877: } else {
0878: return super .uniqueJoin(predList);
0879: }
0880: }
0881:
0882: /**
0883: * Return the restriction list from this node.
0884: *
0885: * @return The restriction list from this node.
0886: */
0887: PredicateList getRestrictionList() {
0888: return restrictionList;
0889: }
0890:
0891: /**
0892: * Return the user specified join strategy, if any for this table.
0893: *
0894: * @return The user specified join strategy, if any for this table.
0895: */
0896: String getUserSpecifiedJoinStrategy() {
0897: if (childResult instanceof FromTable) {
0898: return ((FromTable) childResult)
0899: .getUserSpecifiedJoinStrategy();
0900: } else {
0901: return userSpecifiedJoinStrategy;
0902: }
0903: }
0904:
0905: /**
0906: * Prints the sub-nodes of this object. See QueryTreeNode.java for
0907: * how tree printing is supposed to work.
0908: *
0909: * @param depth The depth of this node in the tree
0910: */
0911:
0912: public void printSubNodes(int depth) {
0913: if (SanityManager.DEBUG) {
0914: super .printSubNodes(depth);
0915:
0916: if (restriction != null) {
0917: printLabel(depth, "restriction: ");
0918: restriction.treePrint(depth + 1);
0919: }
0920:
0921: if (restrictionList != null) {
0922: printLabel(depth, "restrictionList: ");
0923: restrictionList.treePrint(depth + 1);
0924: }
0925:
0926: if (projectSubquerys != null) {
0927: printLabel(depth, "projectSubquerys: ");
0928: projectSubquerys.treePrint(depth + 1);
0929: }
0930:
0931: if (restrictSubquerys != null) {
0932: printLabel(depth, "restrictSubquerys: ");
0933: restrictSubquerys.treePrint(depth + 1);
0934: }
0935: }
0936: }
0937:
0938: /**
0939: * Put a ProjectRestrictNode on top of each FromTable in the FromList.
0940: * ColumnReferences must continue to point to the same ResultColumn, so
0941: * that ResultColumn must percolate up to the new PRN. However,
0942: * that ResultColumn will point to a new expression, a VirtualColumnNode,
0943: * which points to the FromTable and the ResultColumn that is the source for
0944: * the ColumnReference.
0945: * (The new PRN will have the original of the ResultColumnList and
0946: * the ResultColumns from that list. The FromTable will get shallow copies
0947: * of the ResultColumnList and its ResultColumns. ResultColumn.expression
0948: * will remain at the FromTable, with the PRN getting a new
0949: * VirtualColumnNode for each ResultColumn.expression.)
0950: * We then project out the non-referenced columns. If there are no referenced
0951: * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
0952: * whose expression is 1.
0953: *
0954: * @param numTables Number of tables in the DML Statement
0955: * @param gbl The group by list, if any
0956: * @param fromList The from list, if any
0957: *
0958: * @return The generated ProjectRestrictNode atop the original FromTable.
0959: *
0960: * @exception StandardException Thrown on error
0961: */
0962:
0963: public ResultSetNode preprocess(int numTables, GroupByList gbl,
0964: FromList fromList) throws StandardException {
0965: childResult = childResult.preprocess(numTables, gbl, fromList);
0966:
0967: /* Build the referenced table map */
0968: referencedTableMap = (JBitSet) childResult
0969: .getReferencedTableMap().clone();
0970:
0971: return this ;
0972: }
0973:
0974: /**
0975: * Push expressions down to the first ResultSetNode which can do expression
0976: * evaluation and has the same referenced table map.
0977: * RESOLVE - This means only pushing down single table expressions to
0978: * ProjectRestrictNodes today. Once we have a better understanding of how
0979: * the optimizer will work, we can push down join clauses.
0980: *
0981: * @param predicateList The PredicateList.
0982: *
0983: * @exception StandardException Thrown on error
0984: */
0985: public void pushExpressions(PredicateList predicateList)
0986: throws StandardException {
0987: PredicateList pushPList = null;
0988:
0989: if (SanityManager.DEBUG)
0990: SanityManager.ASSERT(predicateList != null,
0991: "predicateList is expected to be non-null");
0992:
0993: /* Push single table predicates down to the left of an outer
0994: * join, if possible. (We need to be able to walk an entire
0995: * join tree.)
0996: */
0997: if (childResult instanceof JoinNode) {
0998: ((FromTable) childResult).pushExpressions(predicateList);
0999:
1000: }
1001:
1002: /* Build a list of the single table predicates that we can push down */
1003: pushPList = predicateList
1004: .getPushablePredicates(referencedTableMap);
1005:
1006: /* If this is a PRN above a SelectNode, probably due to a
1007: * view or derived table which couldn't be flattened, then see
1008: * if we can push any of the predicates which just got pushed
1009: * down to our level into the SelectNode.
1010: */
1011: if (pushPList != null && (childResult instanceof SelectNode)) {
1012: pushPList.pushExpressionsIntoSelect(
1013: (SelectNode) childResult, false);
1014: }
1015:
1016: /* DERBY-649: Push simple predicates into Unions. It would be up to UnionNode
1017: * to decide if these predicates can be pushed further into underlying SelectNodes
1018: * or UnionNodes. Note, we also keep the predicateList at this
1019: * ProjectRestrictNode in case the predicates are not pushable or only
1020: * partially pushable.
1021: *
1022: * It is possible to expand this optimization in UnionNode later.
1023: */
1024: if (pushPList != null && (childResult instanceof UnionNode))
1025: ((UnionNode) childResult).pushExpressions(pushPList);
1026:
1027: if (restrictionList == null) {
1028: restrictionList = pushPList;
1029: } else if (pushPList != null && pushPList.size() != 0) {
1030: /* Concatenate the 2 PredicateLists */
1031: restrictionList.destructiveAppend(pushPList);
1032: }
1033:
1034: /* RESOLVE - this looks like the place to try to try to push the
1035: * predicates through the ProjectRestrict. Seems like we should
1036: * "rebind" the column references and reset the referenced table maps
1037: * in restrictionList and then call childResult.pushExpressions() on
1038: * restrictionList.
1039: */
1040: }
1041:
1042: /**
1043: * Add a new predicate to the list. This is useful when doing subquery
1044: * transformations, when we build a new predicate with the left side of
1045: * the subquery operator and the subquery's result column.
1046: *
1047: * @param predicate The predicate to add
1048: *
1049: * @return ResultSetNode The new top of the tree.
1050: *
1051: * @exception StandardException Thrown on error
1052: */
1053: public ResultSetNode addNewPredicate(Predicate predicate)
1054: throws StandardException {
1055: if (restrictionList == null) {
1056: restrictionList = (PredicateList) getNodeFactory().getNode(
1057: C_NodeTypes.PREDICATE_LIST, getContextManager());
1058: }
1059: restrictionList.addPredicate(predicate);
1060: return this ;
1061: }
1062:
1063: /**
1064: * Evaluate whether or not the subquery in a FromSubquery is flattenable.
1065: * Currently, a FSqry is flattenable if all of the following are true:
1066: * o Subquery is a SelectNode.
1067: * o It contains no top level subqueries. (RESOLVE - we can relax this)
1068: * o It does not contain a group by or having clause
1069: * o It does not contain aggregates.
1070: *
1071: * @param fromList The outer from list
1072: *
1073: * @return boolean Whether or not the FromSubquery is flattenable.
1074: */
1075: public boolean flattenableInFromSubquery(FromList fromList) {
1076: /* Flattening currently involves merging predicates and FromLists.
1077: * We don't have a FromList, so we can't flatten for now.
1078: */
1079: /* RESOLVE - this will introduce yet another unnecessary PRN */
1080: return false;
1081: }
1082:
1083: /**
1084: * Ensure that the top of the RSN tree has a PredicateList.
1085: *
1086: * @param numTables The number of tables in the query.
1087: * @return ResultSetNode A RSN tree with a node which has a PredicateList on top.
1088: *
1089: * @exception StandardException Thrown on error
1090: */
1091: public ResultSetNode ensurePredicateList(int numTables)
1092: throws StandardException {
1093: return this ;
1094: }
1095:
1096: /**
1097: * Optimize this ProjectRestrictNode.
1098: *
1099: * @param dataDictionary The DataDictionary to use for optimization
1100: * @param predicates The PredicateList to optimize. This should
1101: * be a join predicate.
1102: * @param outerRows The number of outer joining rows
1103: *
1104: * @return ResultSetNode The top of the optimized subtree
1105: *
1106: * @exception StandardException Thrown on error
1107: */
1108:
1109: public ResultSetNode optimize(DataDictionary dataDictionary,
1110: PredicateList predicates, double outerRows)
1111: throws StandardException {
1112: /* We need to implement this method since a PRN can appear above a
1113: * SelectNode in a query tree.
1114: */
1115: childResult = childResult.optimize(dataDictionary,
1116: restrictionList, outerRows);
1117:
1118: Optimizer optimizer = getOptimizer((FromList) getNodeFactory()
1119: .getNode(C_NodeTypes.FROM_LIST,
1120: getNodeFactory().doJoinOrderOptimization(),
1121: this , getContextManager()), predicates,
1122: dataDictionary, (RequiredRowOrdering) null);
1123:
1124: // RESOLVE: SHOULD FACTOR IN THE NON-OPTIMIZABLE PREDICATES THAT
1125: // WERE NOT PUSHED DOWN
1126: costEstimate = optimizer.newCostEstimate();
1127:
1128: costEstimate.setCost(childResult.getCostEstimate()
1129: .getEstimatedCost(), childResult.getCostEstimate()
1130: .rowCount(), childResult.getCostEstimate()
1131: .singleScanRowCount());
1132:
1133: return this ;
1134: }
1135:
1136: /**
1137: * Get the CostEstimate for this ProjectRestrictNode.
1138: *
1139: * @return The CostEstimate for this ProjectRestrictNode, which is
1140: * the cost estimate for the child node.
1141: */
1142: public CostEstimate getCostEstimate() {
1143: /*
1144: ** The cost estimate will be set here if either optimize() or
1145: ** optimizeIt() was called on this node. It's also possible
1146: ** that optimization was done directly on the child node,
1147: ** in which case the cost estimate will be null here.
1148: */
1149: if (costEstimate == null)
1150: return childResult.getCostEstimate();
1151: else {
1152: return costEstimate;
1153: }
1154: }
1155:
1156: /**
1157: * Get the final CostEstimate for this ProjectRestrictNode.
1158: *
1159: * @return The final CostEstimate for this ProjectRestrictNode, which is
1160: * the final cost estimate for the child node.
1161: */
1162: public CostEstimate getFinalCostEstimate() throws StandardException {
1163: if (finalCostEstimate != null)
1164: // we already set it, so just return it.
1165: return finalCostEstimate;
1166:
1167: // If the child result set is an Optimizable, then this node's
1168: // final cost is that of the child. Otherwise, this node must
1169: // hold "trulyTheBestAccessPath" for it's child so we pull
1170: // the final cost from there.
1171: if (childResult instanceof Optimizable)
1172: finalCostEstimate = childResult.getFinalCostEstimate();
1173: else
1174: finalCostEstimate = getTrulyTheBestAccessPath()
1175: .getCostEstimate();
1176:
1177: return finalCostEstimate;
1178: }
1179:
1180: /**
1181: * For joins, the tree will be (nodes are left out if the clauses
1182: * are empty):
1183: *
1184: * ProjectRestrictResultSet -- for the having and the select list
1185: * SortResultSet -- for the group by list
1186: * ProjectRestrictResultSet -- for the where and the select list (if no group or having)
1187: * the result set for the fromList
1188: *
1189: *
1190: * @exception StandardException Thrown on error
1191: */
1192: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
1193: throws StandardException {
1194: if (SanityManager.DEBUG)
1195: SanityManager.ASSERT(resultColumns != null,
1196: "Tree structure bad");
1197:
1198: generateMinion(acb, mb, false);
1199: }
1200:
1201: /**
1202: * General logic shared by Core compilation.
1203: *
1204: * @param acb The ExpressionClassBuilder for the class being built
1205: * @param mb The method the expression will go into
1206: *
1207: *
1208: * @exception StandardException Thrown on error
1209: */
1210:
1211: public void generateResultSet(ExpressionClassBuilder acb,
1212: MethodBuilder mb) throws StandardException {
1213: generateMinion(acb, mb, true);
1214: }
1215:
1216: /**
1217: * Logic shared by generate() and generateResultSet().
1218: *
1219: * @param acb The ExpressionClassBuilder for the class being built
1220: * @param mb The method the expression will go into
1221: *
1222: * @exception StandardException Thrown on error
1223: */
1224:
1225: private void generateMinion(ExpressionClassBuilder acb,
1226: MethodBuilder mb, boolean genChildResultSet)
1227: throws StandardException {
1228:
1229: /* If this ProjectRestrict doesn't do anything, bypass its generation.
1230: * (Remove any true and true predicates first, as they could be left
1231: * by the like transformation.)
1232: */
1233: if (restrictionList != null && restrictionList.size() > 0) {
1234: restrictionList.eliminateBooleanTrueAndBooleanTrue();
1235: }
1236:
1237: if (nopProjectRestrict()) {
1238: generateNOPProjectRestrict();
1239: if (genChildResultSet)
1240: childResult.generateResultSet(acb, mb);
1241: else
1242: childResult.generate((ActivationClassBuilder) acb, mb);
1243: costEstimate = childResult.getFinalCostEstimate();
1244: return;
1245: }
1246:
1247: // build up the tree.
1248:
1249: /* Put the predicates back into the tree */
1250: if (restrictionList != null) {
1251: constantRestriction = restrictionList
1252: .restoreConstantPredicates();
1253: // Remove any redundant predicates before restoring
1254: restrictionList.removeRedundantPredicates();
1255: restriction = restrictionList.restorePredicates();
1256: /* Allow the restrictionList to get garbage collected now
1257: * that we're done with it.
1258: */
1259: restrictionList = null;
1260: }
1261:
1262: // for the restriction, we generate an exprFun
1263: // that evaluates the expression of the clause
1264: // against the current row of the child's result.
1265: // if the restriction is empty, simply pass null
1266: // to optimize for run time performance.
1267:
1268: // generate the function and initializer:
1269: // Note: Boolean lets us return nulls (boolean would not)
1270: // private Boolean exprN()
1271: // {
1272: // return <<restriction.generate(ps)>>;
1273: // }
1274: // static Method exprN = method pointer to exprN;
1275:
1276: // Map the result columns to the source columns
1277: int[] mapArray = resultColumns.mapSourceColumns();
1278: int mapArrayItem = acb
1279: .addItem(new ReferencedColumnsDescriptorImpl(mapArray));
1280:
1281: /* Will this node do a projection? */
1282: boolean doesProjection = true;
1283:
1284: /* Does a projection unless same # of columns in same order
1285: * as child.
1286: */
1287: if ((!reflectionNeededForProjection())
1288: && mapArray != null
1289: && mapArray.length == childResult.getResultColumns()
1290: .size()) {
1291: /* mapArray entries are 1-based */
1292: int index = 0;
1293: for (; index < mapArray.length; index++) {
1294: if (mapArray[index] != index + 1) {
1295: break;
1296: }
1297: }
1298: if (index == mapArray.length) {
1299: doesProjection = false;
1300: }
1301: }
1302:
1303: /* Generate the ProjectRestrictSet:
1304: * arg1: childExpress - Expression for childResultSet
1305: * arg2: Activation
1306: * arg3: restrictExpress - Expression for restriction
1307: * arg4: projectExpress - Expression for projection
1308: * arg5: resultSetNumber
1309: * arg6: constantExpress - Expression for constant restriction
1310: * (for example, where 1 = 2)
1311: * arg7: mapArrayItem - item # for mapping of source columns
1312: * arg8: reuseResult - whether or not the result row can be reused
1313: * (ie, will it always be the same)
1314: * arg9: doesProjection - does this node do a projection
1315: * arg10: estimated row count
1316: * arg11: estimated cost
1317: * arg12: close method
1318: */
1319:
1320: acb.pushGetResultSetFactoryExpression(mb);
1321: if (genChildResultSet)
1322: childResult.generateResultSet(acb, mb);
1323: else
1324: childResult.generate((ActivationClassBuilder) acb, mb);
1325:
1326: /* Get the next ResultSet #, so that we can number this ResultSetNode, its
1327: * ResultColumnList and ResultSet.
1328: */
1329: assignResultSetNumber();
1330:
1331: /* Set the point of attachment in all subqueries attached
1332: * to this node.
1333: */
1334: if (projectSubquerys != null && projectSubquerys.size() > 0) {
1335: projectSubquerys.setPointOfAttachment(resultSetNumber);
1336: }
1337: if (restrictSubquerys != null && restrictSubquerys.size() > 0) {
1338: restrictSubquerys.setPointOfAttachment(resultSetNumber);
1339: }
1340:
1341: // Load our final cost estimate.
1342: costEstimate = getFinalCostEstimate();
1343:
1344: // if there is no restriction, we just want to pass null.
1345: if (restriction == null) {
1346: mb.pushNull(ClassName.GeneratedMethod);
1347: } else {
1348: // this sets up the method and the static field.
1349: // generates:
1350: // Object userExprFun { }
1351: MethodBuilder userExprFun = acb.newUserExprFun();
1352:
1353: // restriction knows it is returning its value;
1354:
1355: /* generates:
1356: * return <restriction.generate(acb)>;
1357: * and adds it to userExprFun
1358: * NOTE: The explicit cast to DataValueDescriptor is required
1359: * since the restriction may simply be a boolean column or subquery
1360: * which returns a boolean. For example:
1361: * where booleanColumn
1362: */
1363: restriction.generateExpression(acb, userExprFun);
1364: userExprFun.methodReturn();
1365:
1366: // we are done modifying userExprFun, complete it.
1367: userExprFun.complete();
1368:
1369: // restriction is used in the final result set as an access of the new static
1370: // field holding a reference to this new method.
1371: // generates:
1372: // ActivationClass.userExprFun
1373: // which is the static field that "points" to the userExprFun
1374: // that evaluates the where clause.
1375: acb.pushMethodReference(mb, userExprFun);
1376: }
1377:
1378: /* Determine whether or not reflection is needed for the projection.
1379: * Reflection is not needed if all of the columns map directly to source
1380: * columns.
1381: */
1382: if (reflectionNeededForProjection()) {
1383: // for the resultColumns, we generate a userExprFun
1384: // that creates a new row from expressions against
1385: // the current row of the child's result.
1386: // (Generate optimization: see if we can simply
1387: // return the current row -- we could, but don't, optimize
1388: // the function call out and have execution understand
1389: // that a null function pointer means take the current row
1390: // as-is, with the performance trade-off as discussed above.)
1391:
1392: /* Generate the Row function for the projection */
1393: resultColumns.generateCore(acb, mb, false);
1394: } else {
1395: mb.pushNull(ClassName.GeneratedMethod);
1396: }
1397:
1398: mb.push(resultSetNumber);
1399:
1400: // if there is no constant restriction, we just want to pass null.
1401: if (constantRestriction == null) {
1402: mb.pushNull(ClassName.GeneratedMethod);
1403: } else {
1404: // this sets up the method and the static field.
1405: // generates:
1406: // userExprFun { }
1407: MethodBuilder userExprFun = acb.newUserExprFun();
1408:
1409: // restriction knows it is returning its value;
1410:
1411: /* generates:
1412: * return <restriction.generate(acb)>;
1413: * and adds it to userExprFun
1414: * NOTE: The explicit cast to DataValueDescriptor is required
1415: * since the restriction may simply be a boolean column or subquery
1416: * which returns a boolean. For example:
1417: * where booleanColumn
1418: */
1419: constantRestriction.generateExpression(acb, userExprFun);
1420:
1421: userExprFun.methodReturn();
1422:
1423: // we are done modifying userExprFun, complete it.
1424: userExprFun.complete();
1425:
1426: // restriction is used in the final result set as an access
1427: // of the new static field holding a reference to this new method.
1428: // generates:
1429: // ActivationClass.userExprFun
1430: // which is the static field that "points" to the userExprFun
1431: // that evaluates the where clause.
1432: acb.pushMethodReference(mb, userExprFun);
1433: }
1434:
1435: mb.push(mapArrayItem);
1436: mb.push(resultColumns.reusableResult());
1437: mb.push(doesProjection);
1438: mb.push(costEstimate.rowCount());
1439: mb.push(costEstimate.getEstimatedCost());
1440:
1441: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
1442: "getProjectRestrictResultSet",
1443: ClassName.NoPutResultSet, 10);
1444: }
1445:
1446: /**
1447: * Determine whether this ProjectRestrict does anything. If it doesn't
1448: * filter out any rows or columns, it's a No-Op.
1449: *
1450: * @return true if this ProjectRestrict is a No-Op.
1451: */
1452: boolean nopProjectRestrict() {
1453: /*
1454: ** This ProjectRestrictNode is not a No-Op if it does any
1455: ** restriction.
1456: */
1457: if ((restriction != null)
1458: || (restrictionList != null && restrictionList.size() > 0)) {
1459: return false;
1460: }
1461:
1462: ResultColumnList childColumns = childResult.getResultColumns();
1463: ResultColumnList PRNColumns = this .getResultColumns();
1464:
1465: /*
1466: ** The two lists have the same numbers of elements. Are the lists
1467: ** identical? In other words, is the expression in every ResultColumn
1468: ** in the PRN's RCL a ColumnReference that points to the same-numbered
1469: ** column?
1470: */
1471: if (PRNColumns.nopProjection(childColumns))
1472: return true;
1473:
1474: return false;
1475: }
1476:
1477: /**
1478: * Bypass the generation of this No-Op ProjectRestrict, and just generate
1479: * its child result set.
1480: *
1481: * @exception StandardException Thrown on error
1482: */
1483: public void generateNOPProjectRestrict() throws StandardException {
1484: this .getResultColumns().setRedundant();
1485: }
1486:
1487: /**
1488: * Consider materialization for this ResultSet tree if it is valid and cost effective
1489: * (It is not valid if incorrect results would be returned.)
1490: *
1491: * @return Top of the new/same ResultSet tree.
1492: *
1493: * @exception StandardException Thrown on error
1494: */
1495: public ResultSetNode considerMaterialization(JBitSet outerTables)
1496: throws StandardException {
1497: childResult = childResult.considerMaterialization(outerTables);
1498: if (childResult.performMaterialization(outerTables)) {
1499: MaterializeResultSetNode mrsn;
1500: ResultColumnList prRCList;
1501:
1502: /* If the restriction contians a ColumnReference from another
1503: * table then the MRSN must go above the childResult. Otherwise we can put
1504: * it above ourselves. (The later is optimal since projection and restriction
1505: * will only happen once.)
1506: * Put MRSN above PRN if any of the following are true:
1507: * o PRN doesn't have a restriction list
1508: * o PRN's restriction list is empty
1509: * o Table's referenced in PRN's restriction list are a subset of
1510: * table's referenced in PRN's childResult. (NOTE: Rather than construct
1511: * a new, empty JBitSet before checking, we simply clone the childResult's
1512: * referencedTableMap. This is done for code simplicity and will not
1513: * affect the result.)
1514: */
1515: ReferencedTablesVisitor rtv = new ReferencedTablesVisitor(
1516: (JBitSet) childResult.getReferencedTableMap()
1517: .clone());
1518: boolean emptyRestrictionList = (restrictionList == null || restrictionList
1519: .size() == 0);
1520: if (!emptyRestrictionList) {
1521: restrictionList.accept(rtv);
1522: }
1523: if (emptyRestrictionList
1524: || childResult.getReferencedTableMap().contains(
1525: rtv.getTableMap())) {
1526: /* We get a shallow copy of the ResultColumnList and its
1527: * ResultColumns. (Copy maintains ResultColumn.expression for now.)
1528: */
1529: prRCList = resultColumns;
1530: setResultColumns(resultColumns.copyListAndObjects());
1531:
1532: /* Replace ResultColumn.expression with new VirtualColumnNodes
1533: * in the NormalizeResultSetNode's ResultColumnList. (VirtualColumnNodes include
1534: * pointers to source ResultSetNode, this, and source ResultColumn.)
1535: */
1536: prRCList.genVirtualColumnNodes(this , resultColumns);
1537:
1538: /* Finally, we create the new MaterializeResultSetNode */
1539: mrsn = (MaterializeResultSetNode) getNodeFactory()
1540: .getNode(
1541: C_NodeTypes.MATERIALIZE_RESULT_SET_NODE,
1542: this , prRCList, tableProperties,
1543: getContextManager());
1544: // Propagate the referenced table map if it's already been created
1545: if (referencedTableMap != null) {
1546: mrsn
1547: .setReferencedTableMap((JBitSet) referencedTableMap
1548: .clone());
1549: }
1550: return mrsn;
1551: } else {
1552: /* We get a shallow copy of the ResultColumnList and its
1553: * ResultColumns. (Copy maintains ResultColumn.expression for now.)
1554: */
1555: prRCList = childResult.getResultColumns();
1556: childResult.setResultColumns(prRCList
1557: .copyListAndObjects());
1558:
1559: /* Replace ResultColumn.expression with new VirtualColumnNodes
1560: * in the MaterializeResultSetNode's ResultColumnList. (VirtualColumnNodes include
1561: * pointers to source ResultSetNode, this, and source ResultColumn.)
1562: */
1563: prRCList.genVirtualColumnNodes(childResult, childResult
1564: .getResultColumns());
1565:
1566: /* RESOLVE - we need to push single table predicates down so that
1567: * they get applied while building the MaterializeResultSet.
1568: */
1569:
1570: /* Finally, we create the new MaterializeResultSetNode */
1571: mrsn = (MaterializeResultSetNode) getNodeFactory()
1572: .getNode(
1573: C_NodeTypes.MATERIALIZE_RESULT_SET_NODE,
1574: childResult, prRCList, tableProperties,
1575: getContextManager());
1576: // Propagate the referenced table map if it's already been created
1577: if (childResult.getReferencedTableMap() != null) {
1578: mrsn.setReferencedTableMap((JBitSet) childResult
1579: .getReferencedTableMap().clone());
1580: }
1581: childResult = mrsn;
1582: }
1583: }
1584:
1585: return this ;
1586: }
1587:
1588: /**
1589: * Determine whether or not the specified name is an exposed name in
1590: * the current query block.
1591: *
1592: * @param name The specified name to search for as an exposed name.
1593: * @param schemaName Schema name, if non-null.
1594: * @param exactMatch Whether or not we need an exact match on specified schema and table
1595: * names or match on table id.
1596: *
1597: * @return The FromTable, if any, with the exposed name.
1598: *
1599: * @exception StandardException Thrown on error
1600: */
1601: protected FromTable getFromTableByName(String name,
1602: String schemaName, boolean exactMatch)
1603: throws StandardException {
1604: return childResult.getFromTableByName(name, schemaName,
1605: exactMatch);
1606: }
1607:
1608: /**
1609: * Get the lock mode for the target of an update statement
1610: * (a delete or update). The update mode will always be row for
1611: * CurrentOfNodes. It will be table if there is no where clause.
1612: *
1613: * @return The lock mode
1614: */
1615: public int updateTargetLockMode() {
1616: if (restriction != null || constantRestriction != null) {
1617: return TransactionController.MODE_RECORD;
1618: } else {
1619: return childResult.updateTargetLockMode();
1620: }
1621: }
1622:
1623: /**
1624: * Is it possible to do a distinct scan on this ResultSet tree.
1625: * (See SelectNode for the criteria.)
1626: *
1627: * @param distinctColumns the set of distinct columns
1628: * @return Whether or not it is possible to do a distinct scan on this ResultSet tree.
1629: */
1630: boolean isPossibleDistinctScan(Set distinctColumns) {
1631: if (restriction != null
1632: || (restrictionList != null && restrictionList.size() != 0)) {
1633: return false;
1634: }
1635:
1636: HashSet columns = new HashSet();
1637: for (int i = 0; i < resultColumns.size(); i++) {
1638: ResultColumn rc = (ResultColumn) resultColumns.elementAt(i);
1639: BaseColumnNode bc = rc.getBaseColumnNode();
1640: if (bc == null)
1641: return false;
1642: columns.add(bc);
1643: }
1644:
1645: return columns.equals(distinctColumns)
1646: && childResult.isPossibleDistinctScan(distinctColumns);
1647: }
1648:
1649: /**
1650: * Mark the underlying scan as a distinct scan.
1651: */
1652: void markForDistinctScan() {
1653: childResult.markForDistinctScan();
1654: }
1655:
1656: /**
1657: * Accept a visitor, and call v.visit()
1658: * on child nodes as necessary.
1659: *
1660: * @param v the visitor
1661: *
1662: * @exception StandardException on error
1663: */
1664: public Visitable accept(Visitor v) throws StandardException {
1665: if (v.skipChildren(this )) {
1666: return v.visit(this );
1667: }
1668:
1669: Visitable returnNode = super .accept(v);
1670:
1671: if (restriction != null && !v.stopTraversal()) {
1672: restriction = (ValueNode) restriction.accept(v);
1673: }
1674:
1675: if (restrictionList != null && !v.stopTraversal()) {
1676: restrictionList = (PredicateList) restrictionList.accept(v);
1677: }
1678:
1679: return returnNode;
1680: }
1681:
1682: /**
1683: * set the Information gathered from the parent table that is
1684: * required to peform a referential action on dependent table.
1685: */
1686: public void setRefActionInfo(long fkIndexConglomId,
1687: int[] fkColArray, String parentResultSetId,
1688: boolean dependentScan) {
1689: childResult.setRefActionInfo(fkIndexConglomId, fkColArray,
1690: parentResultSetId, dependentScan);
1691: }
1692:
1693: }
|