KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > compile > ProjectRestrictNode


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.ProjectRestrictNode
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.compile;
23
24 import org.apache.derby.iapi.services.context.ContextManager;
25
26 import org.apache.derby.iapi.sql.compile.Optimizable;
27 import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
28 import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
29 import org.apache.derby.iapi.sql.compile.Optimizer;
30 import org.apache.derby.iapi.sql.compile.CostEstimate;
31 import org.apache.derby.iapi.sql.compile.OptimizableList;
32 import org.apache.derby.iapi.sql.compile.Visitable;
33 import org.apache.derby.iapi.sql.compile.Visitor;
34 import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
35 import org.apache.derby.iapi.sql.compile.RowOrdering;
36 import org.apache.derby.iapi.sql.compile.AccessPath;
37 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
38
39 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
40 import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
41
42 import org.apache.derby.iapi.types.DataValueDescriptor;
43
44 import org.apache.derby.iapi.sql.execute.NoPutResultSet;
45
46 import org.apache.derby.iapi.sql.Activation;
47 import org.apache.derby.iapi.sql.ResultSet;
48
49 import org.apache.derby.iapi.error.StandardException;
50 import org.apache.derby.iapi.reference.ClassName;
51
52 import org.apache.derby.iapi.store.access.TransactionController;
53
54 import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
55 import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
56
57 import org.apache.derby.iapi.services.compiler.MethodBuilder;
58
59 import org.apache.derby.iapi.services.loader.GeneratedMethod;
60
61 import org.apache.derby.iapi.services.sanity.SanityManager;
62
63 import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
64 import org.apache.derby.iapi.util.JBitSet;
65 import org.apache.derby.iapi.services.classfile.VMOpcode;
66
67 import java.util.Properties JavaDoc;
68 import java.util.HashSet JavaDoc;
69 import java.util.Set JavaDoc;
70
71 /**
72  * A ProjectRestrictNode represents a result set for any of the basic DML
73  * operations: SELECT, INSERT, UPDATE, and DELETE. For INSERT with
74  * a VALUES clause, restriction will be null. For both INSERT and UPDATE,
75  * the resultColumns in the selectList will contain the names of the columns
76  * being inserted into or updated.
77  *
78  * NOTE: A ProjectRestrictNode extends FromTable since it can exist in a FromList.
79  *
80  * @author Jeff Lichtman
81  */

82
83 public class ProjectRestrictNode extends SingleChildResultSetNode
84 {
85     /**
86      * The ValueNode for the restriction to be evaluated here.
87      */

88     public ValueNode restriction;
89
90     /**
91      * Constant expressions to be evaluated here.
92      */

93     ValueNode constantRestriction = null;
94
95     /**
96      * Restriction as a PredicateList
97      */

98     public PredicateList restrictionList;
99
100     /**
101      * List of subqueries in projection
102      */

103     SubqueryList projectSubquerys;
104
105     /**
106      * List of subqueries in restriction
107      */

108     SubqueryList restrictSubquerys;
109
110     private boolean accessPathModified;
111
112     private boolean accessPathConsidered;
113
114     private boolean childResultOptimized;
115
116     private boolean materialize;
117
118     /* Should we get the table number from this node,
119      * regardless of the class of our child.
120      */

121     private boolean getTableNumberHere;
122
123     /**
124      * Initializer for a ProjectRestrictNode.
125      *
126      * @param childResult The child ResultSetNode
127      * @param projection The result column list for the projection
128      * @param restriction An expression representing the restriction to be
129      * evaluated here.
130      * @param restrictionList Restriction as a PredicateList
131      * @param projectSubquerys List of subqueries in the projection
132      * @param restrictSubquerys List of subqueries in the restriction
133      * @param tableProperties Properties list associated with the table
134      */

135
136     public void init(
137                             Object JavaDoc childResult,
138                             Object JavaDoc projection,
139                             Object JavaDoc restriction,
140                             Object JavaDoc restrictionList,
141                             Object JavaDoc projectSubquerys,
142                             Object JavaDoc restrictSubquerys,
143                             Object JavaDoc tableProperties)
144     {
145         super.init(childResult, tableProperties);
146         resultColumns = (ResultColumnList) projection;
147         this.restriction = (ValueNode) restriction;
148         this.restrictionList = (PredicateList) restrictionList;
149         this.projectSubquerys = (SubqueryList) projectSubquerys;
150         this.restrictSubquerys = (SubqueryList) restrictSubquerys;
151
152         /* A PRN will only hold the tableProperties for
153          * a result set tree if its child is not an
154          * optimizable. Otherwise, the properties will
155          * be transferred down to the child.
156          */

157         if (tableProperties != null &&
158              (childResult instanceof Optimizable))
159         {
160             ((Optimizable) childResult).setProperties(getProperties());
161             setProperties((Properties JavaDoc) null);
162         }
163     }
164
165     /*
166      * Optimizable interface
167      */

168
169     /**
170         @see Optimizable#nextAccessPath
171         @exception StandardException Thrown on error
172      */

173     public boolean nextAccessPath(Optimizer optimizer,
174                                     OptimizablePredicateList predList,
175                                     RowOrdering rowOrdering)
176             throws StandardException
177     {
178         /*
179         ** If the child result set is an optimizable, let it choose its next
180         ** access path. If it is not an optimizable, we have to tell the
181         ** caller that there is an access path the first time we are called
182         ** for this position in the join order, and that there are no more
183         ** access paths for subsequent calls for this position in the join
184         ** order. The startOptimizing() method is called once on each
185         ** optimizable when it is put into a join position.
186         */

187         if (childResult instanceof Optimizable)
188         {
189             return ((Optimizable) childResult).nextAccessPath(optimizer,
190                                                                 restrictionList,
191                                                                 rowOrdering);
192         }
193         else
194         {
195             return super.nextAccessPath(optimizer, predList, rowOrdering);
196         }
197     }
198
199     /** @see Optimizable#rememberAsBest
200         @exception StandardException Thrown on error
201      */

202     public void rememberAsBest(int planType, Optimizer optimizer)
203         throws StandardException
204     {
205         super.rememberAsBest(planType, optimizer);
206         if (childResult instanceof Optimizable)
207             ((Optimizable) childResult).rememberAsBest(planType, optimizer);
208     }
209
210     /* Don't print anything for a PRN, as their
211      * child has the interesting info.
212      */

213     void printRememberingBestAccessPath(int planType, AccessPath bestPath)
214     {
215     }
216
217     /** @see Optimizable#startOptimizing */
218     public void startOptimizing(Optimizer optimizer, RowOrdering rowOrdering)
219     {
220         if (childResult instanceof Optimizable)
221         {
222             ((Optimizable) childResult).startOptimizing(optimizer, rowOrdering);
223         }
224         else
225         {
226             accessPathConsidered = false;
227
228             super.startOptimizing(optimizer, rowOrdering);
229         }
230     }
231
232     /** @see Optimizable#getTableNumber */
233     public int getTableNumber()
234     {
235         /* GROSS HACK - We need to get the tableNumber after
236          * calling modifyAccessPaths() on the child when doing
237          * a hash join on an arbitrary result set. The problem
238          * is that the child will always be an optimizable at this
239          * point. So, we 1st check to see if we should get it from
240          * this node. (We set the boolean to true in the appropriate
241          * place in modifyAccessPaths().)
242          */

243         if (getTableNumberHere)
244         {
245             return super.getTableNumber();
246         }
247
248         if (childResult instanceof Optimizable)
249             return ((Optimizable) childResult).getTableNumber();
250
251         return super.getTableNumber();
252     }
253
254     /**
255      * @see Optimizable#optimizeIt
256      *
257      * @exception StandardException Thrown on error
258      */

259     public CostEstimate optimizeIt(
260                             Optimizer optimizer,
261                             OptimizablePredicateList predList,
262                             CostEstimate outerCost,
263                             RowOrdering rowOrdering)
264             throws StandardException
265     {
266         /*
267         ** RESOLVE: Most types of Optimizables only implement estimateCost(),
268         ** and leave it up to optimizeIt() in FromTable to figure out the
269         ** total cost of the join. A ProjectRestrict can have a non-Optimizable
270         ** child, though, in which case we want to tell the child the
271         ** number of outer rows - it could affect the join strategy
272         ** significantly. So we implement optimizeIt() here, which overrides
273         ** the optimizeIt() in FromTable. This assumes that the join strategy
274         ** for which this join node is the inner table is a nested loop join,
275         ** which will not be a valid assumption when we implement other
276         ** strategies like materialization (hash join can work only on
277         ** base tables). The join strategy for a base table under a
278         ** ProjectRestrict is set in the base table itself.
279         */

280
281         CostEstimate childCost;
282
283         costEstimate = getCostEstimate(optimizer);
284
285         /*
286         ** Don't re-optimize a child result set that has already been fully
287         ** optimized. For example, if the child result set is a SelectNode,
288         ** it will be changed to a ProjectRestrictNode, which we don't want
289         ** to re-optimized.
290         */

291         // NOTE: TO GET THE RIGHT COST, THE CHILD RESULT MAY HAVE TO BE
292
// OPTIMIZED MORE THAN ONCE, BECAUSE THE NUMBER OF OUTER ROWS
293
// MAY BE DIFFERENT EACH TIME.
294
// if (childResultOptimized)
295
// return costEstimate;
296

297         // It's possible that a call to optimize the left/right will cause
298
// a new "truly the best" plan to be stored in the underlying base
299
// tables. If that happens and then we decide to skip that plan
300
// (which we might do if the call to "considerCost()" below decides
301
// the current path is infeasible or not the best) we need to be
302
// able to revert back to the "truly the best" plans that we had
303
// saved before we got here. So with this next call we save the
304
// current plans using "this" node as the key. If needed, we'll
305
// then make the call to revert the plans in OptimizerImpl's
306
// getNextDecoratedPermutation() method.
307
updateBestPlanMap(ADD_PLAN, this);
308
309         /* If the childResult is instanceof Optimizable, then we optimizeIt.
310          * Otherwise, we are going into a new query block. If the new query
311          * block has already had its access path modified, then there is
312          * nothing to do. Otherwise, we must begin the optimization process
313          * anew on the new query block.
314          */

315         if (childResult instanceof Optimizable)
316         {
317             childCost = ((Optimizable) childResult).optimizeIt(
318                                                             optimizer,
319                                                             restrictionList,
320                                                             outerCost,
321                                                             rowOrdering);
322             /* Copy child cost to this node's cost */
323             costEstimate.setCost(
324                             childCost.getEstimatedCost(),
325                             childCost.rowCount(),
326                             childCost.singleScanRowCount());
327
328
329             // Note: we don't call "optimizer.considerCost()" here because
330
// a) the child will make that call as part of its own
331
// "optimizeIt()" work above, and b) the child might have
332
// different criteria for "considering" (i.e. rejecting or
333
// accepting) a plan's cost than this ProjectRestrictNode does--
334
// and we don't want to override the child's decision. So as
335
// with most operations in this class, if the child is an
336
// Optimizable, we just let it do its own work and make its
337
// own decisions.
338
}
339         else if ( ! accessPathModified)
340         {
341             if (SanityManager.DEBUG)
342             {
343                 if (! ((childResult instanceof SelectNode) ||
344                                  (childResult instanceof RowResultSetNode)))
345                 {
346                     SanityManager.THROWASSERT(
347                         "childResult is expected to be instanceof " +
348                         "SelectNode or RowResultSetNode - it is a " +
349                         childResult.getClass().getName());
350                 }
351             }
352             childResult = childResult.optimize(optimizer.getDataDictionary(),
353                                                restrictionList,
354                                                outerCost.rowCount());
355
356             /* Copy child cost to this node's cost */
357             childCost = childResult.costEstimate;
358
359             costEstimate.setCost(
360                             childCost.getEstimatedCost(),
361                             childCost.rowCount(),
362                             childCost.singleScanRowCount());
363
364             /* Note: Prior to the fix for DERBY-781 we had calls here
365              * to set the cost estimate for BestAccessPath and
366              * BestSortAvoidancePath to equal costEstimate. That used
367              * to be okay because prior to DERBY-781 we would only
368              * get here once (per join order) for a given SelectNode/
369              * RowResultSetNode and thus we could safely say that the
370              * costEstimate from the most recent call to "optimize()"
371              * was the best one so far (because we knew that we would
372              * only call childResult.optimize() once). Now that we
373              * support hash joins with subqueries, though, we can get
374              * here twice per join order: once when the optimizer is
375              * considering a nested loop join with this PRN, and once
376              * when it is considering a hash join. This means we can't
377              * just arbitrarily use the cost estimate for the most recent
378              * "optimize()" as the best cost because that may not
379              * be accurate--it's possible that the above call to
380              * childResult.optimize() was for a hash join, but that
381              * we were here once before (namely for nested loop) and
382              * the cost of the nested loop is actually less than
383              * the cost of the hash join. In that case it would
384              * be wrong to use costEstimate as the cost of the "best"
385              * paths because it (costEstimate) holds the cost of
386              * the hash join, not of the nested loop join. So with
387              * DERBY-781 the following calls were removed:
388              * getBestAccessPath().setCostEstimate(costEstimate);
389              * getBestSortAvoidancePath().setCostEstimate(costEstimate);
390              * If costEstimate *does* actually hold the estimate for
391              * the best path so far, then we will set BestAccessPath
392              * and BestSortAvoidancePath as needed in the following
393              * call to "considerCost".
394              */

395
396             // childResultOptimized = true;
397

398             /* RESOLVE - ARBITRARYHASHJOIN - Passing restriction list here, as above, is correct.
399              * However, passing predList makes the following work:
400              * select * from t1, (select * from t2) c properties joinStrategy = hash where t1.c1 = c.c1;
401              * The following works with restrictionList:
402              * select * from t1, (select c1 + 0 from t2) c(c1) properties joinStrategy = hash where t1.c1 = c.c1;
403              */

404             optimizer.considerCost(this, restrictionList, getCostEstimate(), outerCost);
405         }
406
407         return costEstimate;
408     }
409
410     /**
411      * @see Optimizable#feasibleJoinStrategy
412      *
413      * @exception StandardException Thrown on error
414      */

415     public boolean feasibleJoinStrategy(OptimizablePredicateList predList,
416                                         Optimizer optimizer)
417                     throws StandardException
418     {
419         AccessPath ap;
420
421         /* The child being an Optimizable is a special case. In that
422          * case, we want to get the current access path and join strategy
423          * from the child. Otherwise, we want to get it from this node.
424          */

425         if (childResult instanceof Optimizable)
426         {
427             // With DERBY-805 it's possible that, when considering a nested
428
// loop join with this PRN, we pushed predicates down into the
429
// child if the child is a UNION node. At this point, though, we
430
// may be considering doing a hash join with this PRN instead of a
431
// nested loop join, and if that's the case we need to pull any
432
// predicates back up so that they can be searched for equijoins
433
// that will in turn make the hash join possible. So that's what
434
// the next call does. Two things to note: 1) if no predicates
435
// were pushed, this call is a no-op; and 2) if we get here when
436
// considering a nested loop join, the predicates that we pull
437
// here (if any) will be re-pushed for subsequent costing/
438
// optimization as necessary (see OptimizerImpl.costPermutation(),
439
// which will call this class's optimizeIt() method and that's
440
// where the predicates are pushed down again).
441
if (childResult instanceof UnionNode)
442                 ((UnionNode)childResult).pullOptPredicates(restrictionList);
443
444             return ((Optimizable) childResult).
445                 feasibleJoinStrategy(restrictionList, optimizer);
446         }
447         else
448         {
449             return super.feasibleJoinStrategy(restrictionList, optimizer);
450         }
451     }
452
453     /** @see Optimizable#getCurrentAccessPath */
454     public AccessPath getCurrentAccessPath()
455     {
456         if (childResult instanceof Optimizable)
457             return ((Optimizable) childResult).getCurrentAccessPath();
458
459         return super.getCurrentAccessPath();
460     }
461
462     /** @see Optimizable#getBestAccessPath */
463     public AccessPath getBestAccessPath()
464     {
465         if (childResult instanceof Optimizable)
466             return ((Optimizable) childResult).getBestAccessPath();
467
468         return super.getBestAccessPath();
469     }
470
471     /** @see Optimizable#getBestSortAvoidancePath */
472     public AccessPath getBestSortAvoidancePath()
473     {
474         if (childResult instanceof Optimizable)
475             return ((Optimizable) childResult).getBestSortAvoidancePath();
476
477         return super.getBestSortAvoidancePath();
478     }
479
480     /** @see Optimizable#getTrulyTheBestAccessPath */
481     public AccessPath getTrulyTheBestAccessPath()
482     {
483         /* The childResult will always be an Optimizable
484          * during code generation. If the childResult was
485          * not an Optimizable during optimization, then this node
486          * will have the truly the best access path, so we want to
487          * return it from this node, rather than traversing the tree.
488          * This can happen for non-flattenable derived tables.
489          * Anyway, we note this state when modifying the access paths.
490          */

491         if (hasTrulyTheBestAccessPath)
492         {
493             return super.getTrulyTheBestAccessPath();
494         }
495
496         if (childResult instanceof Optimizable)
497             return ((Optimizable) childResult).getTrulyTheBestAccessPath();
498
499         return super.getTrulyTheBestAccessPath();
500     }
501
502     /** @see Optimizable#rememberSortAvoidancePath */
503     public void rememberSortAvoidancePath()
504     {
505         if (childResult instanceof Optimizable)
506             ((Optimizable) childResult).rememberSortAvoidancePath();
507         else
508             super.rememberSortAvoidancePath();
509     }
510
511     /** @see Optimizable#considerSortAvoidancePath */
512     public boolean considerSortAvoidancePath()
513     {
514         if (childResult instanceof Optimizable)
515             return ((Optimizable) childResult).considerSortAvoidancePath();
516
517         return super.considerSortAvoidancePath();
518     }
519
520     /**
521      * @see Optimizable#pushOptPredicate
522      *
523      * @exception StandardException Thrown on error
524      */

525
526     public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate)
527             throws StandardException
528     {
529         if (SanityManager.DEBUG)
530         {
531             SanityManager.ASSERT(optimizablePredicate instanceof Predicate,
532                 "optimizablePredicate expected to be instanceof Predicate");
533             SanityManager.ASSERT(! optimizablePredicate.hasSubquery() &&
534                                  ! optimizablePredicate.hasMethodCall(),
535                 "optimizablePredicate either has a subquery or a method call");
536         }
537
538         /* Add the matching predicate to the restrictionList */
539         if (restrictionList == null)
540         {
541             restrictionList = (PredicateList) getNodeFactory().getNode(
542                                                     C_NodeTypes.PREDICATE_LIST,
543                                                     getContextManager());
544         }
545         restrictionList.addPredicate((Predicate) optimizablePredicate);
546
547         /* Remap all of the ColumnReferences to point to the
548          * source of the values.
549          */

550         Predicate pred = (Predicate)optimizablePredicate;
551
552         /* If the predicate is scoped then the call to "remapScopedPred()"
553          * will do the necessary remapping for us and will return true;
554          * otherwise, we'll just do the normal remapping here.
555          */

556         if (!pred.remapScopedPred())
557         {
558             RemapCRsVisitor rcrv = new RemapCRsVisitor(true);
559             pred.getAndNode().accept(rcrv);
560         }
561
562         return true;
563     }
564
565     /**
566      * @see Optimizable#pullOptPredicates
567      *
568      * @exception StandardException Thrown on error
569      */

570     public void pullOptPredicates(
571                                 OptimizablePredicateList optimizablePredicates)
572                     throws StandardException
573     {
574         if (restrictionList != null)
575         {
576             // Pull up any predicates that may have been pushed further
577
// down the tree during optimization.
578
if (childResult instanceof UnionNode)
579                 ((UnionNode)childResult).pullOptPredicates(restrictionList);
580
581             RemapCRsVisitor rcrv = new RemapCRsVisitor(false);
582             for (int i = restrictionList.size() - 1; i >= 0; i--)
583             {
584                 OptimizablePredicate optPred =
585                     restrictionList.getOptPredicate(i);
586                 ((Predicate) optPred).getAndNode().accept(rcrv);
587                 optimizablePredicates.addOptPredicate(optPred);
588                 restrictionList.removeOptPredicate(i);
589             }
590         }
591     }
592
593     /**
594      * @see Optimizable#modifyAccessPath
595      *
596      * @exception StandardException Thrown on error
597      */

598     public Optimizable modifyAccessPath(JBitSet outerTables)
599         throws StandardException
600     {
601         boolean origChildOptimizable = true;
602
603         /* It is okay to optimize most nodes multiple times. However,
604          * modifying the access path is something that should only be done
605          * once per node. One reason for this is that the predicate list
606          * will be empty after the 1st call, and we assert that it should
607          * be non-empty. Multiple calls to modify the access path can
608          * occur when there is a non-flattenable FromSubquery (or view).
609          */

610         if (accessPathModified)
611         {
612             return this;
613         }
614
615         /*
616         ** Do nothing if the child result set is not optimizable, as there
617         ** can be nothing to modify.
618         */

619         boolean alreadyPushed = false;
620         if ( ! (childResult instanceof Optimizable))
621         {
622             // Remember that the original child was not Optimizable
623
origChildOptimizable = false;
624
625             /* When we optimized the child we passed in our restriction list
626              * so that scoped predicates could be pushed further down the
627              * tree. We need to do the same when modifying the access
628              * paths to ensure we generate the same plans the optimizer
629              * chose.
630              */

631             childResult = childResult.modifyAccessPaths(restrictionList);
632
633             /* Mark this node as having the truly ... for
634              * the underlying tree.
635              */

636             hasTrulyTheBestAccessPath = true;
637
638             /* Replace this PRN with a HRN if we are doing a hash join */
639             if (trulyTheBestAccessPath.getJoinStrategy().isHashJoin())
640             {
641                 if (SanityManager.DEBUG)
642                 {
643                     SanityManager.ASSERT(restrictionList != null,
644                         "restrictionList expected to be non-null");
645                     SanityManager.ASSERT(restrictionList.size() != 0,
646                             "restrictionList.size() expected to be non-zero");
647                 }
648                 /* We're doing a hash join on an arbitary result set.
649                  * We need to get the table number from this node when
650                  * dividing up the restriction list for a hash join.
651                  * We need to explicitly remember this.
652                  */

653                 getTableNumberHere = true;
654             }
655             else
656             {
657                 /* We consider materialization into a temp table as a last step.
658                  * Currently, we only materialize VTIs that are inner tables
659                  * and can't be instantiated multiple times. In the future we
660                  * will consider materialization as a cost based option.
661                  */

662                 return (Optimizable) considerMaterialization(outerTables);
663             }
664         }
665
666         /* If the child is not a FromBaseTable, then we want to
667          * keep going down the tree. (Nothing to do at this node.)
668          */

669         else if (!(childResult instanceof FromBaseTable))
670         {
671             /* Make sure that we have a join strategy */
672             if (trulyTheBestAccessPath.getJoinStrategy() == null)
673             {
674                 trulyTheBestAccessPath = (AccessPathImpl) ((Optimizable) childResult).getTrulyTheBestAccessPath();
675             }
676
677             // If the childResult is a SetOperatorNode (esp. a UnionNode),
678
// then it's possible that predicates in our restrictionList are
679
// supposed to be pushed further down the tree (as of DERBY-805).
680
// We passed the restrictionList down when we optimized the child
681
// so that the relevant predicates could be pushed further as part
682
// of the optimization process; so now that we're finalizing the
683
// paths, we need to do the same thing: i.e. pass restrictionList
684
// down so that the predicates that need to be pushed further
685
// _can_ be pushed further.
686
if (childResult instanceof SetOperatorNode) {
687                 childResult = (ResultSetNode)
688                     ((SetOperatorNode) childResult).modifyAccessPath(
689                         outerTables, restrictionList);
690
691                 // Take note of the fact that we already pushed predicates
692
// as part of the modifyAccessPaths call. This is necessary
693
// because there may still be predicates in restrictionList
694
// that we intentionally decided not to push (ex. if we're
695
// going to do hash join then we chose to not push the join
696
// predicates). Whatever the reason for not pushing the
697
// predicates, we have to make sure we don't inadvertenly
698
// push them later (esp. as part of the "pushUsefulPredicates"
699
// call below).
700
alreadyPushed = true;
701             }
702             else {
703                 childResult =
704                     (ResultSetNode) ((FromTable) childResult).
705                         modifyAccessPath(outerTables);
706             }
707         }
708
709         // If we're doing a hash join with _this_ PRN (as opposed to
710
// with this PRN's child) then we don't attempt to push
711
// predicates down. There are two reasons for this: 1)
712
// we don't want to push the equijoin predicate that is
713
// required for the hash join, and 2) if we're doing a
714
// hash join then we're going to materialize this node,
715
// but if we push predicates before materialization, we
716
// can end up with incorrect results (esp. missing rows).
717
// So don't push anything in this case.
718
boolean hashJoinWithThisPRN = hasTrulyTheBestAccessPath &&
719             (trulyTheBestAccessPath.getJoinStrategy() != null) &&
720             trulyTheBestAccessPath.getJoinStrategy().isHashJoin();
721
722         if ((restrictionList != null) && !alreadyPushed && !hashJoinWithThisPRN)
723         {
724             restrictionList.pushUsefulPredicates((Optimizable) childResult);
725         }
726
727         /*
728         ** The optimizer's decision on the access path for the child result
729         ** set may require the generation of extra result sets. For
730         ** example, if it chooses an index, we need an IndexToBaseRowNode
731         ** above the FromBaseTable (and the FromBaseTable has to change
732         ** its column list to match that of the index.
733         */

734         if (origChildOptimizable)
735         {
736             childResult = childResult.changeAccessPath();
737         }
738         accessPathModified = true;
739
740         /*
741         ** Replace this PRN with a HTN if a hash join
742         ** is being done at this node. (Hash join on a scan
743         ** is a special case and is handled at the FBT.)
744         */

745         if (trulyTheBestAccessPath.getJoinStrategy() != null &&
746             trulyTheBestAccessPath.getJoinStrategy().isHashJoin())
747         {
748             return replaceWithHashTableNode();
749         }
750
751         /* We consider materialization into a temp table as a last step.
752          * Currently, we only materialize VTIs that are inner tables
753          * and can't be instantiated multiple times. In the future we
754          * will consider materialization as a cost based option.
755          */

756         return (Optimizable) considerMaterialization(outerTables);
757     }
758
759     /**
760      * This method creates a HashTableNode between the PRN and
761      * it's child when the optimizer chooses hash join on an
762      * arbitrary (non-FBT) result set tree.
763      * We divide up the restriction list into 3 parts and
764      * distribute those parts as described below.
765      *
766      * @return The new (same) top of our result set tree.
767      * @exception StandardException Thrown on error
768      */

769     private Optimizable replaceWithHashTableNode()
770         throws StandardException
771     {
772         // If this PRN has TTB access path for its child, store that access
773
// path in the child here, so that we can find it later when it
774
// comes time to generate qualifiers for the hash predicates (we
775
// need the child's access path when generating qualifiers; if we
776
// don't pass the path down here, the child won't be able to find
777
// it).
778
if (hasTrulyTheBestAccessPath)
779         {
780             ((FromTable)childResult).trulyTheBestAccessPath =
781                 (AccessPathImpl)getTrulyTheBestAccessPath();
782
783             // If the child itself is another SingleChildResultSetNode
784
// (which is also what a ProjectRestrictNode is), then tell
785
// it that it is now holding TTB path for it's own child. Again,
786
// this info is needed so that child knows where to find the
787
// access path at generation time.
788
if (childResult instanceof SingleChildResultSetNode)
789             {
790                 ((SingleChildResultSetNode)childResult)
791                     .hasTrulyTheBestAccessPath = hasTrulyTheBestAccessPath;
792
793                 // While we're at it, add the PRN's table number to the
794
// child's referenced map so that we can find the equijoin
795
// predicate. We have to do this because the predicate
796
// will be referencing the PRN's tableNumber, not the
797
// child's--and since we use the child as the target
798
// when searching for hash keys (as can be seen in
799
// HashJoinStrategy.divideUpPredicateLists()), the child
800
// should know what this PRN's table number is. This
801
// is somewhat bizarre since the child doesn't
802
// actually "reference" this PRN, but since the child's
803
// reference map is used when searching for the equijoin
804
// predicate (see "buildTableNumList" in
805
// BinaryRelationalOperatorNode), this is the simplest
806
// way to pass this PRN's table number down.
807
childResult.getReferencedTableMap().set(tableNumber);
808             }
809         }
810
811         /* We want to divide the predicate list into 3 separate lists -
812          * o predicates against the source of the hash table, which will
813          * be applied on the way into the hash table (searchRestrictionList)
814          * o join clauses which are qualifiers and get applied to the
815          * rows in the hash table on a probe (joinRestrictionList)
816          * o non-qualifiers involving both tables which will get
817          * applied after a row gets returned from the HTRS (nonQualifiers)
818          *
819          * We do some unnecessary work when doing this as we want to reuse
820          * as much existing code as possible. The code that we are reusing
821          * was originally built for hash scans, hence the unnecessary
822          * requalification list.
823          */

824         PredicateList searchRestrictionList =
825                                 (PredicateList) getNodeFactory().getNode(
826                                                     C_NodeTypes.PREDICATE_LIST,
827                                                     getContextManager());
828         PredicateList joinQualifierList =
829                                 (PredicateList) getNodeFactory().getNode(
830                                                     C_NodeTypes.PREDICATE_LIST,
831                                                     getContextManager());
832         PredicateList requalificationRestrictionList =
833                                 (PredicateList) getNodeFactory().getNode(
834                                                     C_NodeTypes.PREDICATE_LIST,
835                                                     getContextManager());
836         trulyTheBestAccessPath.getJoinStrategy().divideUpPredicateLists(
837                                             this,
838                                             restrictionList,
839                                             searchRestrictionList,
840                                             joinQualifierList,
841                                             requalificationRestrictionList,
842                                             getDataDictionary());
843
844         /* Break out the non-qualifiers from HTN's join qualifier list and make that
845          * the new restriction list for this PRN.
846          */

847         restrictionList = (PredicateList) getNodeFactory().getNode(
848                                             C_NodeTypes.PREDICATE_LIST,
849                                             getContextManager());
850         /* For non-base table, we remove first 2 lists from requal list to avoid adding duplicates.
851          */

852         for (int i = 0; i < searchRestrictionList.size(); i++)
853             requalificationRestrictionList.removeOptPredicate((Predicate) searchRestrictionList.elementAt(i));
854         for (int i = 0; i < joinQualifierList.size(); i++)
855             requalificationRestrictionList.removeOptPredicate((Predicate) joinQualifierList.elementAt(i));
856
857         joinQualifierList.transferNonQualifiers(this, restrictionList); //purify joinQual list
858
requalificationRestrictionList.copyPredicatesToOtherList(restrictionList); //any residual
859

860         ResultColumnList htRCList;
861
862         /* We get a shallow copy of the child's ResultColumnList and its
863          * ResultColumns. (Copy maintains ResultColumn.expression for now.)
864          */

865         htRCList = childResult.getResultColumns();
866         childResult.setResultColumns(htRCList.copyListAndObjects());
867
868         /* Replace ResultColumn.expression with new VirtualColumnNodes
869          * in the HTN's ResultColumnList. (VirtualColumnNodes include
870          * pointers to source ResultSetNode, this, and source ResultColumn.)
871          * NOTE: We don't want to mark the underlying RCs as referenced, otherwise
872          * we won't be able to project out any of them.
873          */

874         htRCList.genVirtualColumnNodes(childResult, childResult.getResultColumns(), false);
875
876         /* The CRs for this side of the join in both the searchRestrictionList
877          * the joinQualifierList now point to the HTN's RCL. We need them
878          * to point to the RCL in the child of the HTN. (We skip doing this for
879          * the joinQualifierList as the code to generate the Qualifiers does not
880          * care.)
881          */

882         RemapCRsVisitor rcrv = new RemapCRsVisitor(true);
883         searchRestrictionList.accept(rcrv);
884
885         /* We can finally put the HTN between ourself and our old child. */
886         childResult = (ResultSetNode) getNodeFactory().getNode(
887                                             C_NodeTypes.HASH_TABLE_NODE,
888                                             childResult,
889                                             tableProperties,
890                                             htRCList,
891                                             searchRestrictionList,
892                                             joinQualifierList,
893                                             trulyTheBestAccessPath,
894                                             getCostEstimate(),
895                                             projectSubquerys,
896                                             restrictSubquerys,
897                                             hashKeyColumns(),
898                                             getContextManager());
899         return this;
900     }
901
902     /** @see Optimizable#verifyProperties
903      * @exception StandardException Thrown on error
904      */

905     public void verifyProperties(DataDictionary dDictionary)
906         throws StandardException
907     {
908         /* Table properties can be attached to this node if
909          * its child is not an optimizable, otherwise they
910          * are attached to its child.
911          */

912
913         if (childResult instanceof Optimizable)
914         {
915             ((Optimizable) childResult).verifyProperties(dDictionary);
916         }
917         else
918         {
919             super.verifyProperties(dDictionary);
920         }
921     }
922
923     /**
924      * @see Optimizable#legalJoinOrder
925      */

926     public boolean legalJoinOrder(JBitSet assignedTableMap)
927     {
928         if (childResult instanceof Optimizable)
929         {
930             return ((Optimizable) childResult).legalJoinOrder(assignedTableMap);
931         }
932         else
933         {
934             return true;
935         }
936     }
937
938     /**
939      * @see Optimizable#uniqueJoin
940      *
941      * @exception StandardException Thrown on error
942      */

943     public double uniqueJoin(OptimizablePredicateList predList)
944                     throws StandardException
945     {
946         if (childResult instanceof Optimizable)
947         {
948             return ((Optimizable) childResult).uniqueJoin(predList);
949         }
950         else
951         {
952             return super.uniqueJoin(predList);
953         }
954     }
955
956     /**
957      * Return the restriction list from this node.
958      *
959      * @return The restriction list from this node.
960      */

961     PredicateList getRestrictionList()
962     {
963         return restrictionList;
964     }
965
966     /**
967      * Return the user specified join strategy, if any for this table.
968      *
969      * @return The user specified join strategy, if any for this table.
970      */

971     String JavaDoc getUserSpecifiedJoinStrategy()
972     {
973         if (childResult instanceof FromTable)
974         {
975             return ((FromTable) childResult).getUserSpecifiedJoinStrategy();
976         }
977         else
978         {
979             return userSpecifiedJoinStrategy;
980         }
981     }
982
983     /**
984      * Prints the sub-nodes of this object. See QueryTreeNode.java for
985      * how tree printing is supposed to work.
986      *
987      * @param depth The depth of this node in the tree
988      */

989
990     public void printSubNodes(int depth)
991     {
992         if (SanityManager.DEBUG)
993         {
994             super.printSubNodes(depth);
995
996             if (restriction != null)
997             {
998                 printLabel(depth, "restriction: ");
999                 restriction.treePrint(depth + 1);
1000            }
1001
1002            if (restrictionList != null)
1003            {
1004                printLabel(depth, "restrictionList: ");
1005                restrictionList.treePrint(depth + 1);
1006            }
1007
1008            if (projectSubquerys != null)
1009            {
1010                printLabel(depth, "projectSubquerys: ");
1011                projectSubquerys.treePrint(depth + 1);
1012            }
1013
1014            if (restrictSubquerys != null)
1015            {
1016                printLabel(depth, "restrictSubquerys: ");
1017                restrictSubquerys.treePrint(depth + 1);
1018            }
1019        }
1020    }
1021
1022    /**
1023     * Put a ProjectRestrictNode on top of each FromTable in the FromList.
1024     * ColumnReferences must continue to point to the same ResultColumn, so
1025     * that ResultColumn must percolate up to the new PRN. However,
1026     * that ResultColumn will point to a new expression, a VirtualColumnNode,
1027     * which points to the FromTable and the ResultColumn that is the source for
1028     * the ColumnReference.
1029     * (The new PRN will have the original of the ResultColumnList and
1030     * the ResultColumns from that list. The FromTable will get shallow copies
1031     * of the ResultColumnList and its ResultColumns. ResultColumn.expression
1032     * will remain at the FromTable, with the PRN getting a new
1033     * VirtualColumnNode for each ResultColumn.expression.)
1034     * We then project out the non-referenced columns. If there are no referenced
1035     * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
1036     * whose expression is 1.
1037     *
1038     * @param numTables Number of tables in the DML Statement
1039     * @param gbl The group by list, if any
1040     * @param fromList The from list, if any
1041     *
1042     * @return The generated ProjectRestrictNode atop the original FromTable.
1043     *
1044     * @exception StandardException Thrown on error
1045     */

1046
1047    public ResultSetNode preprocess(int numTables,
1048                                    GroupByList gbl,
1049                                    FromList fromList)
1050                                throws StandardException
1051    {
1052        childResult = childResult.preprocess(numTables, gbl, fromList);
1053
1054        /* Build the referenced table map */
1055        referencedTableMap = (JBitSet) childResult.getReferencedTableMap().clone();
1056
1057        return this;
1058    }
1059
1060    /**
1061     * Push expressions down to the first ResultSetNode which can do expression
1062     * evaluation and has the same referenced table map.
1063     * RESOLVE - This means only pushing down single table expressions to
1064     * ProjectRestrictNodes today. Once we have a better understanding of how
1065     * the optimizer will work, we can push down join clauses.
1066     *
1067     * @param predicateList The PredicateList.
1068     *
1069     * @exception StandardException Thrown on error
1070     */

1071    public void pushExpressions(PredicateList predicateList)
1072                    throws StandardException
1073    {
1074        PredicateList pushPList = null;
1075
1076        if (SanityManager.DEBUG)
1077        SanityManager.ASSERT(predicateList != null,
1078                             "predicateList is expected to be non-null");
1079
1080        /* Push single table predicates down to the left of an outer
1081         * join, if possible. (We need to be able to walk an entire
1082         * join tree.)
1083         */

1084        if (childResult instanceof JoinNode)
1085        {
1086            ((FromTable) childResult).pushExpressions(predicateList);
1087
1088        }
1089
1090        /* Build a list of the single table predicates that we can push down */
1091        pushPList = predicateList.getPushablePredicates(referencedTableMap);
1092
1093        /* If this is a PRN above a SelectNode, probably due to a
1094         * view or derived table which couldn't be flattened, then see
1095         * if we can push any of the predicates which just got pushed
1096         * down to our level into the SelectNode.
1097         */

1098        if (pushPList != null && (childResult instanceof SelectNode))
1099        {
1100            pushPList.pushExpressionsIntoSelect((SelectNode) childResult, false);
1101        }
1102
1103        /* DERBY-649: Push simple predicates into Unions. It would be up to UnionNode
1104         * to decide if these predicates can be pushed further into underlying SelectNodes
1105         * or UnionNodes. Note, we also keep the predicateList at this
1106         * ProjectRestrictNode in case the predicates are not pushable or only
1107         * partially pushable.
1108         *
1109         * It is possible to expand this optimization in UnionNode later.
1110         */

1111        if (pushPList != null && (childResult instanceof UnionNode))
1112            ((UnionNode)childResult).pushExpressions(pushPList);
1113
1114        if (restrictionList == null)
1115        {
1116            restrictionList = pushPList;
1117        }
1118        else if (pushPList != null && pushPList.size() != 0)
1119        {
1120            /* Concatenate the 2 PredicateLists */
1121            restrictionList.destructiveAppend(pushPList);
1122        }
1123
1124        /* RESOLVE - this looks like the place to try to try to push the
1125         * predicates through the ProjectRestrict. Seems like we should
1126         * "rebind" the column references and reset the referenced table maps
1127         * in restrictionList and then call childResult.pushExpressions() on
1128         * restrictionList.
1129         */

1130    }
1131
1132    /**
1133     * Add a new predicate to the list. This is useful when doing subquery
1134     * transformations, when we build a new predicate with the left side of
1135     * the subquery operator and the subquery's result column.
1136     *
1137     * @param predicate The predicate to add
1138     *
1139     * @return ResultSetNode The new top of the tree.
1140     *
1141     * @exception StandardException Thrown on error
1142     */

1143    public ResultSetNode addNewPredicate(Predicate predicate)
1144            throws StandardException
1145    {
1146        if (restrictionList == null)
1147        {
1148            restrictionList = (PredicateList) getNodeFactory().getNode(
1149                                                    C_NodeTypes.PREDICATE_LIST,
1150                                                    getContextManager());
1151        }
1152        restrictionList.addPredicate(predicate);
1153        return this;
1154    }
1155
1156    /**
1157     * Evaluate whether or not the subquery in a FromSubquery is flattenable.
1158     * Currently, a FSqry is flattenable if all of the following are true:
1159     * o Subquery is a SelectNode.
1160     * o It contains no top level subqueries. (RESOLVE - we can relax this)
1161     * o It does not contain a group by or having clause
1162     * o It does not contain aggregates.
1163     *
1164     * @param fromList The outer from list
1165     *
1166     * @return boolean Whether or not the FromSubquery is flattenable.
1167     */

1168    public boolean flattenableInFromSubquery(FromList fromList)
1169    {
1170        /* Flattening currently involves merging predicates and FromLists.
1171         * We don't have a FromList, so we can't flatten for now.
1172         */

1173        /* RESOLVE - this will introduce yet another unnecessary PRN */
1174        return false;
1175    }
1176
1177    /**
1178     * Ensure that the top of the RSN tree has a PredicateList.
1179     *
1180     * @param numTables The number of tables in the query.
1181     * @return ResultSetNode A RSN tree with a node which has a PredicateList on top.
1182     *
1183     * @exception StandardException Thrown on error
1184     */

1185    public ResultSetNode ensurePredicateList(int numTables)
1186        throws StandardException
1187    {
1188        return this;
1189    }
1190
1191    /**
1192     * Optimize this ProjectRestrictNode.
1193     *
1194     * @param dataDictionary The DataDictionary to use for optimization
1195     * @param predicates The PredicateList to optimize. This should
1196     * be a join predicate.
1197     * @param outerRows The number of outer joining rows
1198     *
1199     * @return ResultSetNode The top of the optimized subtree
1200     *
1201     * @exception StandardException Thrown on error
1202     */

1203
1204    public ResultSetNode optimize(DataDictionary dataDictionary,
1205                                  PredicateList predicates,
1206                                  double outerRows)
1207                    throws StandardException
1208    {
1209        /* We need to implement this method since a PRN can appear above a
1210         * SelectNode in a query tree.
1211         */

1212        childResult = childResult.optimize(dataDictionary,
1213                                            restrictionList,
1214                                            outerRows);
1215
1216        Optimizer optimizer = getOptimizer(
1217                                (FromList) getNodeFactory().getNode(
1218                                    C_NodeTypes.FROM_LIST,
1219                                    getNodeFactory().doJoinOrderOptimization(),
1220                                    this,
1221                                    getContextManager()),
1222                                predicates,
1223                                dataDictionary,
1224                                (RequiredRowOrdering) null);
1225
1226        // RESOLVE: SHOULD FACTOR IN THE NON-OPTIMIZABLE PREDICATES THAT
1227
// WERE NOT PUSHED DOWN
1228
costEstimate = optimizer.newCostEstimate();
1229
1230        costEstimate.setCost(childResult.getCostEstimate().getEstimatedCost(),
1231                            childResult.getCostEstimate().rowCount(),
1232                            childResult.getCostEstimate().singleScanRowCount());
1233
1234        return this;
1235    }
1236
1237    /**
1238     * Get the CostEstimate for this ProjectRestrictNode.
1239     *
1240     * @return The CostEstimate for this ProjectRestrictNode, which is
1241     * the cost estimate for the child node.
1242     */

1243    public CostEstimate getCostEstimate()
1244    {
1245        /*
1246        ** The cost estimate will be set here if either optimize() or
1247        ** optimizeIt() was called on this node. It's also possible
1248        ** that optimization was done directly on the child node,
1249        ** in which case the cost estimate will be null here.
1250        */

1251        if (costEstimate == null)
1252            return childResult.getCostEstimate();
1253        else
1254        {
1255            return costEstimate;
1256        }
1257    }
1258
1259    /**
1260     * Get the final CostEstimate for this ProjectRestrictNode.
1261     *
1262     * @return The final CostEstimate for this ProjectRestrictNode, which is
1263     * the final cost estimate for the child node.
1264     */

1265    public CostEstimate getFinalCostEstimate()
1266        throws StandardException
1267    {
1268        if (finalCostEstimate != null)
1269        // we already set it, so just return it.
1270
return finalCostEstimate;
1271
1272        // If the child result set is an Optimizable, then this node's
1273
// final cost is that of the child. Otherwise, this node must
1274
// hold "trulyTheBestAccessPath" for it's child so we pull
1275
// the final cost from there.
1276
if (childResult instanceof Optimizable)
1277            finalCostEstimate = childResult.getFinalCostEstimate();
1278        else
1279            finalCostEstimate = getTrulyTheBestAccessPath().getCostEstimate();
1280
1281        return finalCostEstimate;
1282    }
1283
1284    /**
1285     * For joins, the tree will be (nodes are left out if the clauses
1286     * are empty):
1287     *
1288     * ProjectRestrictResultSet -- for the having and the select list
1289     * SortResultSet -- for the group by list
1290     * ProjectRestrictResultSet -- for the where and the select list (if no group or having)
1291     * the result set for the fromList
1292     *
1293     *
1294     * @exception StandardException Thrown on error
1295     */

1296    public void generate(ActivationClassBuilder acb,
1297                                MethodBuilder mb)
1298                            throws StandardException
1299    {
1300        if (SanityManager.DEBUG)
1301        SanityManager.ASSERT(resultColumns != null, "Tree structure bad");
1302
1303        generateMinion( acb, mb, false);
1304    }
1305
1306    /**
1307     * General logic shared by Core compilation.
1308     *
1309     * @param acb The ExpressionClassBuilder for the class being built
1310     * @param mb The method the expression will go into
1311     *
1312     *
1313     * @exception StandardException Thrown on error
1314     */

1315
1316    public void generateResultSet(ExpressionClassBuilder acb,
1317                                           MethodBuilder mb)
1318                                    throws StandardException
1319    {
1320        generateMinion( acb, mb, true);
1321    }
1322
1323    /**
1324     * Logic shared by generate() and generateResultSet().
1325     *
1326     * @param acb The ExpressionClassBuilder for the class being built
1327     * @param mb The method the expression will go into
1328     *
1329     * @exception StandardException Thrown on error
1330     */

1331
1332    private void generateMinion(ExpressionClassBuilder acb,
1333                                     MethodBuilder mb, boolean genChildResultSet)
1334                                    throws StandardException
1335    {
1336
1337        /* If this ProjectRestrict doesn't do anything, bypass its generation.
1338         * (Remove any true and true predicates first, as they could be left
1339         * by the like transformation.)
1340         */

1341        if (restrictionList != null && restrictionList.size() > 0)
1342        {
1343            restrictionList.eliminateBooleanTrueAndBooleanTrue();
1344        }
1345
1346        if (nopProjectRestrict())
1347        {
1348            generateNOPProjectRestrict();
1349            if (genChildResultSet)
1350                childResult.generateResultSet(acb, mb);
1351            else
1352                childResult.generate((ActivationClassBuilder)acb, mb);
1353            costEstimate = childResult.getFinalCostEstimate();
1354            return;
1355        }
1356
1357        // build up the tree.
1358

1359        /* Put the predicates back into the tree */
1360        if (restrictionList != null)
1361        {
1362            constantRestriction = restrictionList.restoreConstantPredicates();
1363            // Remove any redundant predicates before restoring
1364
restrictionList.removeRedundantPredicates();
1365            restriction = restrictionList.restorePredicates();
1366            /* Allow the restrictionList to get garbage collected now
1367             * that we're done with it.
1368             */

1369            restrictionList = null;
1370        }
1371
1372        // for the restriction, we generate an exprFun
1373
// that evaluates the expression of the clause
1374
// against the current row of the child's result.
1375
// if the restriction is empty, simply pass null
1376
// to optimize for run time performance.
1377

1378        // generate the function and initializer:
1379
// Note: Boolean lets us return nulls (boolean would not)
1380
// private Boolean exprN()
1381
// {
1382
// return <<restriction.generate(ps)>>;
1383
// }
1384
// static Method exprN = method pointer to exprN;
1385

1386
1387
1388
1389        // Map the result columns to the source columns
1390
int[] mapArray = resultColumns.mapSourceColumns();
1391        int mapArrayItem = acb.addItem(new ReferencedColumnsDescriptorImpl(mapArray));
1392
1393        /* Will this node do a projection? */
1394        boolean doesProjection = true;
1395
1396        /* Does a projection unless same # of columns in same order
1397         * as child.
1398         */

1399        if ( (! reflectionNeededForProjection()) &&
1400            mapArray != null &&
1401            mapArray.length == childResult.getResultColumns().size())
1402        {
1403            /* mapArray entries are 1-based */
1404            int index = 0;
1405            for ( ; index < mapArray.length; index++)
1406            {
1407                if (mapArray[index] != index + 1)
1408                {
1409                    break;
1410                }
1411            }
1412            if (index == mapArray.length)
1413            {
1414                doesProjection = false;
1415            }
1416        }
1417
1418
1419
1420        /* Generate the ProjectRestrictSet:
1421         * arg1: childExpress - Expression for childResultSet
1422         * arg2: Activation
1423         * arg3: restrictExpress - Expression for restriction
1424         * arg4: projectExpress - Expression for projection
1425         * arg5: resultSetNumber
1426         * arg6: constantExpress - Expression for constant restriction
1427         * (for example, where 1 = 2)
1428         * arg7: mapArrayItem - item # for mapping of source columns
1429         * arg8: reuseResult - whether or not the result row can be reused
1430         * (ie, will it always be the same)
1431         * arg9: doesProjection - does this node do a projection
1432         * arg10: estimated row count
1433         * arg11: estimated cost
1434         * arg12: close method
1435         */

1436
1437        acb.pushGetResultSetFactoryExpression(mb);
1438        if (genChildResultSet)
1439            childResult.generateResultSet(acb, mb);
1440        else
1441            childResult.generate((ActivationClassBuilder)acb, mb);
1442
1443        /* Get the next ResultSet #, so that we can number this ResultSetNode, its
1444         * ResultColumnList and ResultSet.
1445         */

1446        assignResultSetNumber();
1447        
1448        /* Set the point of attachment in all subqueries attached
1449         * to this node.
1450         */

1451        if (projectSubquerys != null && projectSubquerys.size() > 0)
1452        {
1453            projectSubquerys.setPointOfAttachment(resultSetNumber);
1454        }
1455        if (restrictSubquerys != null && restrictSubquerys.size() > 0)
1456        {
1457            restrictSubquerys.setPointOfAttachment(resultSetNumber);
1458        }
1459
1460        // Load our final cost estimate.
1461
costEstimate = getFinalCostEstimate();
1462
1463        // if there is no restriction, we just want to pass null.
1464
if (restriction == null)
1465        {
1466            mb.pushNull(ClassName.GeneratedMethod);
1467        }
1468        else
1469        {
1470            // this sets up the method and the static field.
1471
// generates:
1472
// Object userExprFun { }
1473
MethodBuilder userExprFun = acb.newUserExprFun();
1474
1475            // restriction knows it is returning its value;
1476

1477            /* generates:
1478             * return <restriction.generate(acb)>;
1479             * and adds it to userExprFun
1480             * NOTE: The explicit cast to DataValueDescriptor is required
1481             * since the restriction may simply be a boolean column or subquery
1482             * which returns a boolean. For example:
1483             * where booleanColumn
1484             */

1485            restriction.generateExpression(acb, userExprFun);
1486            userExprFun.methodReturn();
1487
1488            // we are done modifying userExprFun, complete it.
1489
userExprFun.complete();
1490
1491            // restriction is used in the final result set as an access of the new static
1492
// field holding a reference to this new method.
1493
// generates:
1494
// ActivationClass.userExprFun
1495
// which is the static field that "points" to the userExprFun
1496
// that evaluates the where clause.
1497
acb.pushMethodReference(mb, userExprFun);
1498        }
1499
1500        /* Determine whether or not reflection is needed for the projection.
1501         * Reflection is not needed if all of the columns map directly to source
1502         * columns.
1503         */

1504        if (reflectionNeededForProjection())
1505        {
1506            // for the resultColumns, we generate a userExprFun
1507
// that creates a new row from expressions against
1508
// the current row of the child's result.
1509
// (Generate optimization: see if we can simply
1510
// return the current row -- we could, but don't, optimize
1511
// the function call out and have execution understand
1512
// that a null function pointer means take the current row
1513
// as-is, with the performance trade-off as discussed above.)
1514

1515            /* Generate the Row function for the projection */
1516            resultColumns.generateCore(acb, mb, false);
1517        }
1518        else
1519        {
1520            mb.pushNull(ClassName.GeneratedMethod);
1521        }
1522        
1523        mb.push(resultSetNumber);
1524
1525        // if there is no constant restriction, we just want to pass null.
1526
if (constantRestriction == null)
1527        {
1528            mb.pushNull(ClassName.GeneratedMethod);
1529        }
1530        else
1531        {
1532            // this sets up the method and the static field.
1533
// generates:
1534
// userExprFun { }
1535
MethodBuilder userExprFun = acb.newUserExprFun();
1536
1537            // restriction knows it is returning its value;
1538

1539            /* generates:
1540             * return <restriction.generate(acb)>;
1541             * and adds it to userExprFun
1542             * NOTE: The explicit cast to DataValueDescriptor is required
1543             * since the restriction may simply be a boolean column or subquery
1544             * which returns a boolean. For example:
1545             * where booleanColumn
1546             */

1547            constantRestriction.generateExpression(acb, userExprFun);
1548
1549            userExprFun.methodReturn();
1550
1551            // we are done modifying userExprFun, complete it.
1552
userExprFun.complete();
1553
1554            // restriction is used in the final result set as an access
1555
// of the new static field holding a reference to this new method.
1556
// generates:
1557
// ActivationClass.userExprFun
1558
// which is the static field that "points" to the userExprFun
1559
// that evaluates the where clause.
1560
acb.pushMethodReference(mb, userExprFun);
1561        }
1562        
1563        mb.push(mapArrayItem);
1564        mb.push(resultColumns.reusableResult());
1565        mb.push(doesProjection);
1566        mb.push(costEstimate.rowCount());
1567        mb.push(costEstimate.getEstimatedCost());
1568
1569        mb.callMethod(VMOpcode.INVOKEINTERFACE, (String JavaDoc) null, "getProjectRestrictResultSet",
1570                    ClassName.NoPutResultSet, 10);
1571    }
1572
1573    /**
1574     * Determine whether this ProjectRestrict does anything. If it doesn't
1575     * filter out any rows or columns, it's a No-Op.
1576     *
1577     * @return true if this ProjectRestrict is a No-Op.
1578     */

1579    boolean nopProjectRestrict()
1580    {
1581        /*
1582        ** This ProjectRestrictNode is not a No-Op if it does any
1583        ** restriction.
1584        */

1585        if ( (restriction != null) ||
1586             (restrictionList != null && restrictionList.size() > 0) )
1587        {
1588            return false;
1589        }
1590
1591        ResultColumnList childColumns = childResult.getResultColumns();
1592        ResultColumnList PRNColumns = this.getResultColumns();
1593
1594        /*
1595        ** The two lists have the same numbers of elements. Are the lists
1596        ** identical? In other words, is the expression in every ResultColumn
1597        ** in the PRN's RCL a ColumnReference that points to the same-numbered
1598        ** column?
1599        */

1600        if (PRNColumns.nopProjection(childColumns))
1601            return true;
1602
1603        return false;
1604    }
1605
1606    /**
1607     * Bypass the generation of this No-Op ProjectRestrict, and just generate
1608     * its child result set.
1609     *
1610     * @exception StandardException Thrown on error
1611     */

1612    public void generateNOPProjectRestrict()
1613            throws StandardException
1614    {
1615        this.getResultColumns().setRedundant();
1616    }
1617
1618    /**
1619     * Consider materialization for this ResultSet tree if it is valid and cost effective
1620     * (It is not valid if incorrect results would be returned.)
1621     *
1622     * @return Top of the new/same ResultSet tree.
1623     *
1624     * @exception StandardException Thrown on error
1625     */

1626    public ResultSetNode considerMaterialization(JBitSet outerTables)
1627        throws StandardException
1628    {
1629        childResult = childResult.considerMaterialization(outerTables);
1630        if (childResult.performMaterialization(outerTables))
1631        {
1632            MaterializeResultSetNode mrsn;
1633            ResultColumnList prRCList;
1634
1635            /* If the restriction contians a ColumnReference from another
1636             * table then the MRSN must go above the childResult. Otherwise we can put
1637             * it above ourselves. (The later is optimal since projection and restriction
1638             * will only happen once.)
1639             * Put MRSN above PRN if any of the following are true:
1640             * o PRN doesn't have a restriction list
1641             * o PRN's restriction list is empty
1642             * o Table's referenced in PRN's restriction list are a subset of
1643             * table's referenced in PRN's childResult. (NOTE: Rather than construct
1644             * a new, empty JBitSet before checking, we simply clone the childResult's
1645             * referencedTableMap. This is done for code simplicity and will not
1646             * affect the result.)
1647             */

1648            ReferencedTablesVisitor rtv = new ReferencedTablesVisitor(
1649                                                (JBitSet) childResult.getReferencedTableMap().clone());
1650            boolean emptyRestrictionList = (restrictionList == null || restrictionList.size() == 0);
1651            if (! emptyRestrictionList)
1652            {
1653                restrictionList.accept(rtv);
1654            }
1655            if (emptyRestrictionList ||
1656                childResult.getReferencedTableMap().contains(rtv.getTableMap()))
1657            {
1658                /* We get a shallow copy of the ResultColumnList and its
1659                 * ResultColumns. (Copy maintains ResultColumn.expression for now.)
1660                 */

1661                prRCList = resultColumns;
1662                setResultColumns(resultColumns.copyListAndObjects());
1663
1664                /* Replace ResultColumn.expression with new VirtualColumnNodes
1665                 * in the NormalizeResultSetNode's ResultColumnList. (VirtualColumnNodes include
1666                 * pointers to source ResultSetNode, this, and source ResultColumn.)
1667                 */

1668                prRCList.genVirtualColumnNodes(this, resultColumns);
1669
1670                /* Finally, we create the new MaterializeResultSetNode */
1671                mrsn = (MaterializeResultSetNode) getNodeFactory().getNode(
1672                                    C_NodeTypes.MATERIALIZE_RESULT_SET_NODE,
1673                                    this,
1674                                    prRCList,
1675                                    tableProperties,
1676                                    getContextManager());
1677                // Propagate the referenced table map if it's already been created
1678
if (referencedTableMap != null)
1679                {
1680                    mrsn.setReferencedTableMap((JBitSet) referencedTableMap.clone());
1681                }
1682                return mrsn;
1683            }
1684            else
1685            {
1686                /* We get a shallow copy of the ResultColumnList and its
1687                 * ResultColumns. (Copy maintains ResultColumn.expression for now.)
1688                 */

1689                prRCList = childResult.getResultColumns();
1690                childResult.setResultColumns(prRCList.copyListAndObjects());
1691
1692                /* Replace ResultColumn.expression with new VirtualColumnNodes
1693                 * in the MaterializeResultSetNode's ResultColumnList. (VirtualColumnNodes include
1694                 * pointers to source ResultSetNode, this, and source ResultColumn.)
1695                 */

1696                prRCList.genVirtualColumnNodes(childResult, childResult.getResultColumns());
1697
1698                /* RESOLVE - we need to push single table predicates down so that
1699                 * they get applied while building the MaterializeResultSet.
1700                 */

1701
1702                /* Finally, we create the new MaterializeResultSetNode */
1703                mrsn = (MaterializeResultSetNode) getNodeFactory().getNode(
1704                                    C_NodeTypes.MATERIALIZE_RESULT_SET_NODE,
1705                                    childResult,
1706                                    prRCList,
1707                                    tableProperties,
1708                                    getContextManager());
1709                // Propagate the referenced table map if it's already been created
1710
if (childResult.getReferencedTableMap() != null)
1711                {
1712                    mrsn.setReferencedTableMap((JBitSet) childResult.getReferencedTableMap().clone());
1713                }
1714                childResult = mrsn;
1715            }
1716        }
1717
1718        return this;
1719    }
1720
1721    /**
1722     * Determine whether or not the specified name is an exposed name in
1723     * the current query block.
1724     *
1725     * @param name The specified name to search for as an exposed name.
1726     * @param schemaName Schema name, if non-null.
1727     * @param exactMatch Whether or not we need an exact match on specified schema and table
1728     * names or match on table id.
1729     *
1730     * @return The FromTable, if any, with the exposed name.
1731     *
1732     * @exception StandardException Thrown on error
1733     */

1734    protected FromTable getFromTableByName(String JavaDoc name, String JavaDoc schemaName, boolean exactMatch)
1735        throws StandardException
1736    {
1737        return childResult.getFromTableByName(name, schemaName, exactMatch);
1738    }
1739
1740    /**
1741     * Get the lock mode for the target of an update statement
1742     * (a delete or update). The update mode will always be row for
1743     * CurrentOfNodes. It will be table if there is no where clause.
1744     *
1745     * @return The lock mode
1746     */

1747    public int updateTargetLockMode()
1748    {
1749        if (restriction != null || constantRestriction != null)
1750        {
1751            return TransactionController.MODE_RECORD;
1752        }
1753        else
1754        {
1755            return childResult.updateTargetLockMode();
1756        }
1757    }
1758
1759    /**
1760     * Is it possible to do a distinct scan on this ResultSet tree.
1761     * (See SelectNode for the criteria.)
1762     *
1763     * @param distinctColumns the set of distinct columns
1764     * @return Whether or not it is possible to do a distinct scan on this ResultSet tree.
1765     */

1766    boolean isPossibleDistinctScan(Set distinctColumns)
1767    {
1768        if (restriction != null ||
1769            (restrictionList != null && restrictionList.size() != 0))
1770        {
1771            return false;
1772        }
1773
1774        HashSet JavaDoc columns = new HashSet JavaDoc();
1775        for (int i = 0; i < resultColumns.size(); i++) {
1776            ResultColumn rc = (ResultColumn) resultColumns.elementAt(i);
1777            BaseColumnNode bc = rc.getBaseColumnNode();
1778            if (bc == null) return false;
1779            columns.add(bc);
1780        }
1781
1782        return columns.equals(distinctColumns) && childResult.isPossibleDistinctScan(distinctColumns);
1783    }
1784
1785    /**
1786     * Mark the underlying scan as a distinct scan.
1787     */

1788    void markForDistinctScan()
1789    {
1790        childResult.markForDistinctScan();
1791    }
1792
1793
1794    /**
1795     * Accept a visitor, and call v.visit()
1796     * on child nodes as necessary.
1797     *
1798     * @param v the visitor
1799     *
1800     * @exception StandardException on error
1801     */

1802    public Visitable accept(Visitor v)
1803        throws StandardException
1804    {
1805        if (v.skipChildren(this))
1806        {
1807            return v.visit(this);
1808        }
1809
1810        Visitable returnNode = super.accept(v);
1811
1812        if (restriction != null && !v.stopTraversal())
1813        {
1814            restriction = (ValueNode)restriction.accept(v);
1815        }
1816
1817        if (restrictionList != null && !v.stopTraversal())
1818        {
1819            restrictionList = (PredicateList)restrictionList.accept(v);
1820        }
1821
1822        return returnNode;
1823    }
1824
1825
1826
1827    /**
1828     * set the Information gathered from the parent table that is
1829     * required to peform a referential action on dependent table.
1830     */

1831    public void setRefActionInfo(long fkIndexConglomId,
1832                                 int[]fkColArray,
1833                                 String JavaDoc parentResultSetId,
1834                                 boolean dependentScan)
1835    {
1836        childResult.setRefActionInfo(fkIndexConglomId,
1837                                   fkColArray,
1838                                   parentResultSetId,
1839                                   dependentScan);
1840    }
1841
1842}
1843
Popular Tags