KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.FromTable
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.JoinStrategy;
32 import org.apache.derby.iapi.sql.compile.AccessPath;
33 import org.apache.derby.iapi.sql.compile.RowOrdering;
34 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
35
36 import org.apache.derby.iapi.sql.dictionary.*;
37
38 import org.apache.derby.iapi.types.DataTypeDescriptor;
39
40 import org.apache.derby.iapi.error.StandardException;
41 import org.apache.derby.iapi.services.sanity.SanityManager;
42
43 import org.apache.derby.iapi.reference.SQLState;
44 import org.apache.derby.iapi.error.StandardException;
45
46 import org.apache.derby.impl.sql.execute.HashScanResultSet;
47
48 import org.apache.derby.iapi.util.JBitSet;
49 import org.apache.derby.iapi.services.io.FormatableBitSet;
50 import org.apache.derby.iapi.util.StringUtil;
51 import org.apache.derby.catalog.UUID;
52
53 import java.util.Enumeration JavaDoc;
54 import java.util.Properties JavaDoc;
55 import java.util.Vector JavaDoc;
56 import java.util.HashMap JavaDoc;
57
58 /**
59  * A FromTable represents a table in the FROM clause of a DML statement.
60  * It can be either a base table, a subquery or a project restrict.
61  *
62  * @see FromBaseTable
63  * @see FromSubquery
64  * @see ProjectRestrictNode
65  *
66  * @author Jeff Lichtman
67  */

68 abstract class FromTable extends ResultSetNode implements Optimizable
69 {
70     Properties JavaDoc tableProperties;
71     String JavaDoc correlationName;
72     TableName corrTableName;
73     int tableNumber;
74     /* (Query block) level is 0-based. */
75     /* RESOLVE - View resolution will have to update the level within
76      * the view tree.
77      */

78     int level;
79     // hashKeyColumns are 0-based column #s within the row returned by the store for hash scans
80
int[] hashKeyColumns;
81
82     // overrides for hash join
83
int initialCapacity = HashScanResultSet.DEFAULT_INITIAL_CAPACITY;
84     float loadFactor = HashScanResultSet.DEFAULT_LOADFACTOR;
85     int maxCapacity = HashScanResultSet.DEFAULT_MAX_CAPACITY;
86
87     AccessPathImpl currentAccessPath;
88     AccessPathImpl bestAccessPath;
89     AccessPathImpl bestSortAvoidancePath;
90     AccessPathImpl trulyTheBestAccessPath;
91
92     private int joinStrategyNumber;
93
94     protected String JavaDoc userSpecifiedJoinStrategy;
95
96     protected CostEstimate bestCostEstimate;
97
98     private FormatableBitSet refCols;
99
100     private double perRowUsage = -1;
101     
102     private boolean considerSortAvoidancePath;
103
104     /**
105      Set of object->trulyTheBestAccessPath mappings used to keep track
106      of which of this Optimizable's "trulyTheBestAccessPath" was the best
107      with respect to a specific outer query or ancestor node. In the case
108      of an outer query, the object key will be an instance of OptimizerImpl.
109      In the case of an ancestor node, the object key will be that node itself.
110      Each ancestor node or outer query could potentially have a different
111      idea of what this Optimizable's "best access path" is, so we have to
112      keep track of them all.
113     */

114     private HashMap JavaDoc bestPlanMap;
115
116     /** Operations that can be performed on bestPlanMap. */
117     protected static final short REMOVE_PLAN = 0;
118     protected static final short ADD_PLAN = 1;
119     protected static final short LOAD_PLAN = 2;
120
121     /** the original unbound table name */
122     protected TableName origTableName;
123     
124     /**
125      * Initializer for a table in a FROM list.
126      *
127      * @param correlationName The correlation name
128      * @param tableProperties Properties list associated with the table
129      */

130     public void init(Object JavaDoc correlationName, Object JavaDoc tableProperties)
131     {
132         this.correlationName = (String JavaDoc) correlationName;
133         this.tableProperties = (Properties JavaDoc) tableProperties;
134         tableNumber = -1;
135         bestPlanMap = null;
136     }
137
138     /**
139      * Get this table's correlation name, if any.
140      */

141     public String JavaDoc getCorrelationName() { return correlationName; }
142
143     /*
144      * Optimizable interface
145      */

146
147     /**
148      * @see org.apache.derby.iapi.sql.compile.Optimizable#optimizeIt
149      *
150      * @exception StandardException Thrown on error
151      *
152      */

153     public CostEstimate optimizeIt(
154                             Optimizer optimizer,
155                             OptimizablePredicateList predList,
156                             CostEstimate outerCost,
157                             RowOrdering rowOrdering)
158             throws StandardException
159     {
160         // It's possible that a call to optimize the left/right will cause
161
// a new "truly the best" plan to be stored in the underlying base
162
// tables. If that happens and then we decide to skip that plan
163
// (which we might do if the call to "considerCost()" below decides
164
// the current path is infeasible or not the best) we need to be
165
// able to revert back to the "truly the best" plans that we had
166
// saved before we got here. So with this next call we save the
167
// current plans using "this" node as the key. If needed, we'll
168
// then make the call to revert the plans in OptimizerImpl's
169
// getNextDecoratedPermutation() method.
170
updateBestPlanMap(ADD_PLAN, this);
171
172         CostEstimate singleScanCost = estimateCost(predList,
173                                                 (ConglomerateDescriptor) null,
174                                                 outerCost,
175                                                 optimizer,
176                                                 rowOrdering);
177
178         /* Make sure there is a cost estimate to set */
179         getCostEstimate(optimizer);
180
181         setCostEstimate(singleScanCost);
182
183         /* Optimize any subqueries that need to get optimized and
184          * are not optimized any where else. (Like those
185          * in a RowResultSetNode.)
186          */

187         optimizeSubqueries(getDataDictionary(), costEstimate.rowCount());
188
189         /*
190         ** Get the cost of this result set in the context of the whole plan.
191         */

192         getCurrentAccessPath().
193             getJoinStrategy().
194                 estimateCost(
195                             this,
196                             predList,
197                             (ConglomerateDescriptor) null,
198                             outerCost,
199                             optimizer,
200                             getCostEstimate()
201                             );
202
203         optimizer.considerCost(this, predList, getCostEstimate(), outerCost);
204
205         return getCostEstimate();
206     }
207
208     /**
209         @see Optimizable#nextAccessPath
210         @exception StandardException Thrown on error
211      */

212     public boolean nextAccessPath(Optimizer optimizer,
213                                     OptimizablePredicateList predList,
214                                     RowOrdering rowOrdering)
215                     throws StandardException
216     {
217         int numStrat = optimizer.getNumberOfJoinStrategies();
218         boolean found = false;
219         AccessPath ap = getCurrentAccessPath();
220
221         /*
222         ** Most Optimizables have no ordering, so tell the rowOrdering that
223         ** this Optimizable is unordered, if appropriate.
224         */

225         if (userSpecifiedJoinStrategy != null)
226         {
227             /*
228             ** User specified a join strategy, so we should look at only one
229             ** strategy. If there is a current strategy, we have already
230             ** looked at the strategy, so go back to null.
231             */

232             if (ap.getJoinStrategy() != null)
233             {
234                 ap.setJoinStrategy((JoinStrategy) null);
235
236                 found = false;
237             }
238             else
239             {
240                 ap.setJoinStrategy(
241                                 optimizer.getJoinStrategy(userSpecifiedJoinStrategy));
242
243                 if (ap.getJoinStrategy() == null)
244                 {
245                     throw StandardException.newException(SQLState.LANG_INVALID_JOIN_STRATEGY,
246                         userSpecifiedJoinStrategy, getBaseTableName());
247                 }
248
249                 found = true;
250             }
251         }
252         else if (joinStrategyNumber < numStrat)
253         {
254             /* Step through the join strategies. */
255             ap.setJoinStrategy(optimizer.getJoinStrategy(joinStrategyNumber));
256
257             joinStrategyNumber++;
258
259             found = true;
260
261             optimizer.trace(Optimizer.CONSIDERING_JOIN_STRATEGY, tableNumber, 0, 0.0,
262                             ap.getJoinStrategy());
263         }
264
265         /*
266         ** Tell the RowOrdering about columns that are equal to constant
267         ** expressions.
268         */

269         tellRowOrderingAboutConstantColumns(rowOrdering, predList);
270
271         return found;
272     }
273
274     /** Most Optimizables cannot be ordered */
275     protected boolean canBeOrdered()
276     {
277         return false;
278     }
279
280     /** @see Optimizable#getCurrentAccessPath */
281     public AccessPath getCurrentAccessPath()
282     {
283         return currentAccessPath;
284     }
285
286     /** @see Optimizable#getBestAccessPath */
287     public AccessPath getBestAccessPath()
288     {
289         return bestAccessPath;
290     }
291
292     /** @see Optimizable#getBestSortAvoidancePath */
293     public AccessPath getBestSortAvoidancePath()
294     {
295         return bestSortAvoidancePath;
296     }
297
298     /** @see Optimizable#getTrulyTheBestAccessPath */
299     public AccessPath getTrulyTheBestAccessPath()
300     {
301         return trulyTheBestAccessPath;
302     }
303
304     /** @see Optimizable#rememberSortAvoidancePath */
305     public void rememberSortAvoidancePath()
306     {
307         considerSortAvoidancePath = true;
308     }
309
310     /** @see Optimizable#considerSortAvoidancePath */
311     public boolean considerSortAvoidancePath()
312     {
313         return considerSortAvoidancePath;
314     }
315
316     /** @see Optimizable#rememberJoinStrategyAsBest */
317     public void rememberJoinStrategyAsBest(AccessPath ap)
318     {
319         Optimizer optimizer = ap.getOptimizer();
320
321         ap.setJoinStrategy(getCurrentAccessPath().getJoinStrategy());
322
323         optimizer.trace(Optimizer.REMEMBERING_JOIN_STRATEGY, tableNumber, 0, 0.0,
324               getCurrentAccessPath().getJoinStrategy());
325
326         if (ap == bestAccessPath)
327         {
328             optimizer.trace(Optimizer.REMEMBERING_BEST_ACCESS_PATH_SUBSTRING,
329                             tableNumber, 0, 0.0, ap);
330         }
331         else if (ap == bestSortAvoidancePath)
332         {
333             optimizer.trace(Optimizer.REMEMBERING_BEST_SORT_AVOIDANCE_ACCESS_PATH_SUBSTRING,
334                             tableNumber, 0, 0.0, ap);
335         }
336         else
337         {
338             /* We currently get here when optimizing an outer join.
339              * (Problem predates optimizer trace change.)
340              * RESOLVE - fix this at some point.
341             if (SanityManager.DEBUG)
342             {
343                 SanityManager.THROWASSERT(
344                     "unknown access path type");
345             }
346              */

347             optimizer.trace(Optimizer.REMEMBERING_BEST_UNKNOWN_ACCESS_PATH_SUBSTRING,
348                             tableNumber, 0, 0.0, ap);
349         }
350     }
351
352     /** @see Optimizable#getTableDescriptor */
353     public TableDescriptor getTableDescriptor()
354     {
355         if (SanityManager.DEBUG)
356         {
357             SanityManager.THROWASSERT(
358                 "getTableDescriptor() not expected to be called for "
359                 + getClass().toString());
360         }
361
362         return null;
363     }
364
365     /**
366      * @see org.apache.derby.iapi.sql.compile.Optimizable#pushOptPredicate
367      *
368      * @exception StandardException Thrown on error
369      */

370
371     public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate)
372         throws StandardException
373     {
374         return false;
375     }
376
377     /**
378      * @see Optimizable#pullOptPredicates
379      *
380      * @exception StandardException Thrown on error
381      */

382     public void pullOptPredicates(
383                                 OptimizablePredicateList optimizablePredicates)
384                 throws StandardException
385     {
386         /* For most types of Optimizable, do nothing */
387         return;
388     }
389
390     /**
391      * @see Optimizable#modifyAccessPath
392      *
393      * @exception StandardException Thrown on error
394      */

395     public Optimizable modifyAccessPath(JBitSet outerTables) throws StandardException
396     {
397         /* For most types of Optimizable, do nothing */
398         return this;
399     }
400
401     /**
402      * @see Optimizable#isCoveringIndex
403      * @exception StandardException Thrown on error
404      */

405     public boolean isCoveringIndex(ConglomerateDescriptor cd) throws StandardException
406     {
407         return false;
408     }
409
410     /** @see Optimizable#getProperties */
411     public Properties JavaDoc getProperties()
412     {
413         return tableProperties;
414     }
415
416     /** @see Optimizable#setProperties */
417     public void setProperties(Properties JavaDoc tableProperties)
418     {
419         this.tableProperties = tableProperties;
420     }
421
422     /** @see Optimizable#verifyProperties
423      * @exception StandardException Thrown on error
424      */

425     public void verifyProperties(DataDictionary dDictionary)
426         throws StandardException
427     {
428         if (tableProperties == null)
429         {
430             return;
431         }
432         /* Check here for:
433          * invalid properties key
434          * invalid joinStrategy
435          * invalid value for hashInitialCapacity
436          * invalid value for hashLoadFactor
437          * invalid value for hashMaxCapacity
438          */

439         boolean indexSpecified = false;
440         Enumeration JavaDoc e = tableProperties.keys();
441         while (e.hasMoreElements())
442         {
443             String JavaDoc key = (String JavaDoc) e.nextElement();
444             String JavaDoc value = (String JavaDoc) tableProperties.get(key);
445
446             if (key.equals("joinStrategy"))
447             {
448                 userSpecifiedJoinStrategy = StringUtil.SQLToUpperCase(value);
449             }
450             else if (key.equals("hashInitialCapacity"))
451             {
452                 initialCapacity = getIntProperty(value, key);
453
454                 // verify that the specified value is valid
455
if (initialCapacity <= 0)
456                 {
457                     throw StandardException.newException(SQLState.LANG_INVALID_HASH_INITIAL_CAPACITY,
458                             String.valueOf(initialCapacity));
459                 }
460             }
461             else if (key.equals("hashLoadFactor"))
462             {
463                 try
464                 {
465                     loadFactor = Float.valueOf(value).floatValue();
466                 }
467                 catch (NumberFormatException JavaDoc nfe)
468                 {
469                     throw StandardException.newException(SQLState.LANG_INVALID_NUMBER_FORMAT_FOR_OVERRIDE,
470                             value, key);
471                 }
472
473                 // verify that the specified value is valid
474
if (loadFactor <= 0.0 || loadFactor > 1.0)
475                 {
476                     throw StandardException.newException(SQLState.LANG_INVALID_HASH_LOAD_FACTOR,
477                             value);
478                 }
479             }
480             else if (key.equals("hashMaxCapacity"))
481             {
482                 maxCapacity = getIntProperty(value, key);
483
484                 // verify that the specified value is valid
485
if (maxCapacity <= 0)
486                 {
487                     throw StandardException.newException(SQLState.LANG_INVALID_HASH_MAX_CAPACITY,
488                             String.valueOf(maxCapacity));
489                 }
490             }
491             else
492             {
493                 // No other "legal" values at this time
494
throw StandardException.newException(SQLState.LANG_INVALID_FROM_TABLE_PROPERTY, key,
495                     "joinStrategy");
496             }
497         }
498     }
499
500     /** @see Optimizable#getName
501      * @exception StandardException Thrown on error
502      */

503     public String JavaDoc getName() throws StandardException
504     {
505         return getExposedName();
506     }
507
508     /** @see Optimizable#getBaseTableName */
509     public String JavaDoc getBaseTableName()
510     {
511         return "";
512     }
513
514     /** @see Optimizable#convertAbsoluteToRelativeColumnPosition */
515     public int convertAbsoluteToRelativeColumnPosition(int absolutePosition)
516     {
517         return absolutePosition;
518     }
519
520     /** @see Optimizable#updateBestPlanMap */
521     public void updateBestPlanMap(short action,
522         Object JavaDoc planKey) throws StandardException
523     {
524         if (action == REMOVE_PLAN)
525         {
526             if (bestPlanMap != null)
527             {
528                 bestPlanMap.remove(planKey);
529                 if (bestPlanMap.size() == 0)
530                     bestPlanMap = null;
531             }
532
533             return;
534         }
535
536         AccessPath bestPath = getTrulyTheBestAccessPath();
537         AccessPathImpl ap = null;
538         if (action == ADD_PLAN)
539         {
540             // If we get to this method before ever optimizing this node, then
541
// there will be no best path--so there's nothing to do.
542
if (bestPath == null)
543                 return;
544
545             // If the bestPlanMap already exists, search for an
546
// AccessPath for the received key and use that if we can.
547
if (bestPlanMap == null)
548                 bestPlanMap = new HashMap JavaDoc();
549             else
550                 ap = (AccessPathImpl)bestPlanMap.get(planKey);
551
552             // If we don't already have an AccessPath for the key,
553
// create a new one. If the key is an OptimizerImpl then
554
// we might as well pass it in to the AccessPath constructor;
555
// otherwise just pass null.
556
if (ap == null)
557             {
558                 if (planKey instanceof Optimizer)
559                     ap = new AccessPathImpl((Optimizer)planKey);
560                 else
561                     ap = new AccessPathImpl((Optimizer)null);
562             }
563
564             ap.copy(bestPath);
565             bestPlanMap.put(planKey, ap);
566             return;
567         }
568
569         // If we get here, we want to load the best plan from our map
570
// into this Optimizable's trulyTheBestAccessPath field.
571

572         // If we don't have any plans saved, then there's nothing to load.
573
// This can happen if the key is an OptimizerImpl that tried some
574
// join order for which there was no valid plan.
575
if (bestPlanMap == null)
576             return;
577
578         ap = (AccessPathImpl)bestPlanMap.get(planKey);
579
580         // It might be the case that there is no plan stored for
581
// the key, in which case there's nothing to load.
582
if ((ap == null) || (ap.getCostEstimate() == null))
583             return;
584
585         // We found a best plan in our map, so load it into this Optimizable's
586
// trulyTheBestAccessPath field.
587
bestPath.copy(ap);
588         return;
589     }
590
591     /** @see Optimizable#rememberAsBest */
592     public void rememberAsBest(int planType, Optimizer optimizer)
593         throws StandardException
594     {
595         AccessPath bestPath = null;
596
597         switch (planType)
598         {
599           case Optimizer.NORMAL_PLAN:
600             bestPath = getBestAccessPath();
601             break;
602
603           case Optimizer.SORT_AVOIDANCE_PLAN:
604             bestPath = getBestSortAvoidancePath();
605             break;
606
607           default:
608             if (SanityManager.DEBUG)
609             {
610                 SanityManager.THROWASSERT(
611                     "Invalid plan type " + planType);
612             }
613         }
614
615         getTrulyTheBestAccessPath().copy(bestPath);
616
617         // Since we just set trulyTheBestAccessPath for the current
618
// join order of the received optimizer, take note of what
619
// that path is, in case we need to "revert" back to this
620
// path later. See Optimizable.updateBestPlanMap().
621
// Note: Since this call descends all the way down to base
622
// tables, it can be relatively expensive when we have deeply
623
// nested subqueries. So in an attempt to save some work, we
624
// skip the call if this node is a ProjectRestrictNode whose
625
// child is an Optimizable--in that case the ProjectRestrictNode
626
// will in turn call "rememberAsBest" on its child and so
627
// the required call to updateBestPlanMap() will be
628
// made at that time. If we did it here, too, then we would
629
// just end up duplicating the work.
630
if (!(this instanceof ProjectRestrictNode))
631             updateBestPlanMap(ADD_PLAN, optimizer);
632         else
633         {
634             ProjectRestrictNode prn = (ProjectRestrictNode)this;
635             if (!(prn.getChildResult() instanceof Optimizable))
636                 updateBestPlanMap(ADD_PLAN, optimizer);
637         }
638          
639         /* also store the name of the access path; i.e index name/constraint
640          * name if we're using an index to access the base table.
641          */

642         ConglomerateDescriptor cd = bestPath.getConglomerateDescriptor();
643
644         if (isBaseTable())
645         {
646             DataDictionary dd = getDataDictionary();
647             TableDescriptor td = getTableDescriptor();
648             getTrulyTheBestAccessPath().initializeAccessPathName(dd, td);
649         }
650
651         setCostEstimate(bestPath.getCostEstimate());
652
653         bestPath.getOptimizer().trace(Optimizer.REMEMBERING_BEST_ACCESS_PATH,
654                             tableNumber, planType, 0.0, bestPath);
655     }
656
657     /** @see Optimizable#startOptimizing */
658     public void startOptimizing(Optimizer optimizer, RowOrdering rowOrdering)
659     {
660         resetJoinStrategies(optimizer);
661
662         considerSortAvoidancePath = false;
663
664         /*
665         ** If there are costs associated with the best and sort access
666         ** paths, set them to their maximum values, so that any legitimate
667         ** access path will look cheaper.
668         */

669         CostEstimate ce = getBestAccessPath().getCostEstimate();
670
671         if (ce != null)
672             ce.setCost(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
673
674         ce = getBestSortAvoidancePath().getCostEstimate();
675
676         if (ce != null)
677             ce.setCost(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
678
679         if (! canBeOrdered())
680             rowOrdering.addUnorderedOptimizable(this);
681     }
682
683     /**
684      * This method is called when this table is placed in a potential
685      * join order, or when a new conglomerate is being considered.
686      * Set this join strategy number to 0 to indicate that
687      * no join strategy has been considered for this table yet.
688      */

689     protected void resetJoinStrategies(Optimizer optimizer)
690     {
691         joinStrategyNumber = 0;
692         getCurrentAccessPath().setJoinStrategy((JoinStrategy) null);
693     }
694
695     /**
696      * @see Optimizable#estimateCost
697      *
698      * @exception StandardException Thrown on error
699      */

700     public CostEstimate estimateCost(OptimizablePredicateList predList,
701                                     ConglomerateDescriptor cd,
702                                     CostEstimate outerCost,
703                                     Optimizer optimizer,
704                                     RowOrdering rowOrdering)
705             throws StandardException
706     {
707         if (SanityManager.DEBUG)
708         {
709             SanityManager.THROWASSERT(
710              "estimateCost() not expected to be called for " +
711              getClass().toString());
712         }
713
714         return null;
715     }
716
717     /**
718      * Get the final CostEstimate for this FromTable.
719      *
720      * @return The final CostEstimate for this FromTable, which is
721      * the costEstimate of trulyTheBestAccessPath if there is one.
722      * If there's no trulyTheBestAccessPath for this node, then
723      * we just return the value stored in costEstimate as a default.
724      */

725     public CostEstimate getFinalCostEstimate()
726         throws StandardException
727     {
728         // If we already found it, just return it.
729
if (finalCostEstimate != null)
730             return finalCostEstimate;
731
732         if (getTrulyTheBestAccessPath() == null)
733             finalCostEstimate = costEstimate;
734         else
735             finalCostEstimate = getTrulyTheBestAccessPath().getCostEstimate();
736
737         return finalCostEstimate;
738     }
739
740     /** @see Optimizable#isBaseTable */
741     public boolean isBaseTable()
742     {
743         return false;
744     }
745
746     /** @see Optimizable#isMaterializable
747      *
748      * @exception StandardException Thrown on error
749      */

750     public boolean isMaterializable()
751         throws StandardException
752     {
753         /* Derived tables are materializable
754          * iff they are not correlated with an outer query block.
755          */

756
757         HasCorrelatedCRsVisitor visitor = new HasCorrelatedCRsVisitor();
758         accept(visitor);
759         return !(visitor.hasCorrelatedCRs());
760     }
761
762     /** @see Optimizable#supportsMultipleInstantiations */
763     public boolean supportsMultipleInstantiations()
764     {
765         return true;
766     }
767
768     /** @see Optimizable#getTableNumber */
769     public int getTableNumber()
770     {
771         return tableNumber;
772     }
773
774     /** @see Optimizable#hasTableNumber */
775     public boolean hasTableNumber()
776     {
777         return tableNumber >= 0;
778     }
779
780     /** @see Optimizable#forUpdate */
781     public boolean forUpdate()
782     {
783         return false;
784     }
785
786     /** @see Optimizable#initialCapacity */
787     public int initialCapacity()
788     {
789         if (SanityManager.DEBUG)
790         {
791             SanityManager.THROWASSERT("Not expected to be called");
792         }
793
794         return 0;
795     }
796
797     /** @see Optimizable#loadFactor */
798     public float loadFactor()
799     {
800         if (SanityManager.DEBUG)
801         {
802             SanityManager.THROWASSERT("Not expected to be called");
803         }
804
805         return 0.0F;
806     }
807
808     /** @see Optimizable#maxCapacity */
809     public int maxCapacity( JoinStrategy joinStrategy, int maxMemoryPerTable) throws StandardException
810     {
811         return joinStrategy.maxCapacity( maxCapacity, maxMemoryPerTable, getPerRowUsage());
812     }
813
814     private double getPerRowUsage() throws StandardException
815     {
816         if( perRowUsage < 0)
817         {
818             // Do not use getRefCols() because the cached refCols may no longer be valid.
819
FormatableBitSet refCols = resultColumns.getReferencedFormatableBitSet(cursorTargetTable(), true, false);
820             perRowUsage = 0.0;
821
822             /* Add up the memory usage for each referenced column */
823             for (int i = 0; i < refCols.size(); i++)
824             {
825                 if (refCols.isSet(i))
826                 {
827                     ResultColumn rc = (ResultColumn) resultColumns.elementAt(i);
828                     DataTypeDescriptor expressionType = rc.getExpressionType();
829                     if( expressionType != null)
830                         perRowUsage += expressionType.estimatedMemoryUsage();
831                 }
832             }
833
834             /*
835             ** If the proposed conglomerate is a non-covering index, add the
836             ** size of the RowLocation column to the total.
837             **
838             ** NOTE: We don't have a DataTypeDescriptor representing a
839             ** REF column here, so just add a constant here.
840             */

841             ConglomerateDescriptor cd =
842               getCurrentAccessPath().getConglomerateDescriptor();
843             if (cd != null)
844             {
845                 if (cd.isIndex() && ( ! isCoveringIndex(cd) ) )
846                 {
847                     // workaround for a jikes bug. Can't directly reference a
848
// double with a value of 12.0 in this classfile.
849
double baseIndexUsage = 1.0;
850                     perRowUsage += ( baseIndexUsage + 11 );
851                 }
852             }
853         }
854         return perRowUsage ;
855     } // end of getPerRowUsage
856

857     /** @see Optimizable#hashKeyColumns */
858     public int[] hashKeyColumns()
859     {
860         if (SanityManager.DEBUG)
861         {
862             SanityManager.ASSERT(hashKeyColumns != null,
863                 "hashKeyColumns expected to be non-null");
864         }
865
866         return hashKeyColumns;
867     }
868
869     /** @see Optimizable#setHashKeyColumns */
870     public void setHashKeyColumns(int[] columnNumbers)
871     {
872         hashKeyColumns = columnNumbers;
873     }
874
875     /**
876      * @see Optimizable#feasibleJoinStrategy
877      *
878      * @exception StandardException Thrown on error
879      */

880     public boolean feasibleJoinStrategy(OptimizablePredicateList predList,
881                                         Optimizer optimizer)
882                     throws StandardException
883     {
884         return getCurrentAccessPath().getJoinStrategy().
885                                 feasible(this, predList, optimizer);
886     }
887
888     /** @see Optimizable#memoryUsageOK */
889     public boolean memoryUsageOK( double rowCount, int maxMemoryPerTable)
890             throws StandardException
891     {
892         /*
893         ** Don't enforce maximum memory usage for a user-specified join
894         ** strategy.
895         */

896         if( userSpecifiedJoinStrategy != null)
897             return true;
898
899         int intRowCount = (rowCount > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) rowCount;
900         return intRowCount <= maxCapacity( getCurrentAccessPath().getJoinStrategy(), maxMemoryPerTable);
901     }
902
903     /**
904      * @see Optimizable#legalJoinOrder
905      */

906     public boolean legalJoinOrder(JBitSet assignedTableMap)
907     {
908         // Only those subclasses with dependencies need to override this.
909
return true;
910     }
911
912     /**
913      * @see Optimizable#getNumColumnsReturned
914      */

915     public int getNumColumnsReturned()
916     {
917         return resultColumns.size();
918     }
919
920     /**
921      * @see Optimizable#isTargetTable
922      */

923     public boolean isTargetTable()
924     {
925         return false;
926     }
927
928     /**
929      * @see Optimizable#isOneRowScan
930      *
931      * @exception StandardException Thrown on error
932      */

933     public boolean isOneRowScan()
934         throws StandardException
935     {
936         /* We simply return isOneRowResultSet() for all
937          * subclasses except for EXISTS FBT where
938          * the semantics differ between 1 row per probe
939          * and whether or not there can be more than 1
940          * rows that qualify on a scan.
941          */

942         return isOneRowResultSet();
943     }
944
945     /**
946      * @see Optimizable#initAccessPaths
947      */

948     public void initAccessPaths(Optimizer optimizer)
949     {
950         if (currentAccessPath == null)
951         {
952             currentAccessPath = new AccessPathImpl(optimizer);
953         }
954         if (bestAccessPath == null)
955         {
956             bestAccessPath = new AccessPathImpl(optimizer);
957         }
958         if (bestSortAvoidancePath == null)
959         {
960             bestSortAvoidancePath = new AccessPathImpl(optimizer);
961         }
962         if (trulyTheBestAccessPath == null)
963         {
964             trulyTheBestAccessPath = new AccessPathImpl(optimizer);
965         }
966     }
967
968     /**
969      * @see Optimizable#uniqueJoin
970      *
971      * @exception StandardException Thrown on error
972      */

973     public double uniqueJoin(OptimizablePredicateList predList)
974                         throws StandardException
975     {
976         return -1.0;
977     }
978
979     private FormatableBitSet getRefCols()
980     {
981         if (refCols == null)
982             refCols = resultColumns.getReferencedFormatableBitSet(cursorTargetTable(), true, false);
983
984         return refCols;
985     }
986
987
988     /**
989      * Return the user specified join strategy, if any for this table.
990      *
991      * @return The user specified join strategy, if any for this table.
992      */

993     String JavaDoc getUserSpecifiedJoinStrategy()
994     {
995         if (tableProperties == null)
996         {
997             return null;
998         }
999
1000        return tableProperties.getProperty("joinStrategy");
1001    }
1002
1003    /**
1004     * Is this a table that has a FOR UPDATE
1005     * clause. Overridden by FromBaseTable.
1006     *
1007     * @return true/false
1008     */

1009    protected boolean cursorTargetTable()
1010    {
1011        return false;
1012    }
1013
1014    protected CostEstimate getCostEstimate(Optimizer optimizer)
1015    {
1016        if (costEstimate == null)
1017        {
1018            costEstimate = optimizer.newCostEstimate();
1019        }
1020        return costEstimate;
1021    }
1022
1023    /*
1024    ** This gets a cost estimate for doing scratch calculations. Typically,
1025    ** it will hold the estimated cost of a conglomerate. If the optimizer
1026    ** decides the scratch cost is lower than the best cost estimate so far,
1027    ** it will copy the scratch cost to the non-scratch cost estimate,
1028    ** which is allocated above.
1029    */

1030    protected CostEstimate getScratchCostEstimate(Optimizer optimizer)
1031    {
1032        if (scratchCostEstimate == null)
1033        {
1034            scratchCostEstimate = optimizer.newCostEstimate();
1035        }
1036
1037        return scratchCostEstimate;
1038    }
1039
1040    /**
1041     * Set the cost estimate in this node to the given cost estimate.
1042     */

1043    protected void setCostEstimate(CostEstimate newCostEstimate)
1044    {
1045        costEstimate = getCostEstimate();
1046
1047        costEstimate.setCost(newCostEstimate);
1048    }
1049
1050    /**
1051     * Assign the cost estimate in this node to the given cost estimate.
1052     */

1053    protected void assignCostEstimate(CostEstimate newCostEstimate)
1054    {
1055        costEstimate = newCostEstimate;
1056    }
1057
1058    /**
1059     * Convert this object to a String. See comments in QueryTreeNode.java
1060     * for how this should be done for tree printing.
1061     *
1062     * @return This object as a String
1063     */

1064
1065    public String JavaDoc toString()
1066    {
1067        if (SanityManager.DEBUG)
1068        {
1069            return "correlation Name: " + correlationName + "\n" +
1070                (corrTableName != null ?
1071                    corrTableName.toString() : "null") + "\n" +
1072                "tableNumber " + tableNumber + "\n" +
1073                "level " + level + "\n" +
1074                super.toString();
1075        }
1076        else
1077        {
1078            return "";
1079        }
1080    }
1081
1082    /**
1083     * Return a ResultColumnList with all of the columns in this table.
1084     * (Used in expanding '*'s.)
1085     * NOTE: Since this method is for expanding a "*" in the SELECT list,
1086     * ResultColumn.expression will be a ColumnReference.
1087     *
1088     * @param allTableName The qualifier on the "*"
1089     *
1090     * @return ResultColumnList List of result columns from this table.
1091     *
1092     * @exception StandardException Thrown on error
1093     */

1094    public ResultColumnList getResultColumnsForList(TableName allTableName,
1095                                                ResultColumnList inputRcl,
1096                                                TableName tableName)
1097            throws StandardException
1098    {
1099        ResultColumnList rcList = null;
1100        ResultColumn resultColumn;
1101        ValueNode valueNode;
1102        String JavaDoc columnName;
1103        TableName exposedName;
1104        TableName toCompare;
1105
1106        /* If allTableName is non-null, then we must check to see if it matches
1107         * our exposed name.
1108         */

1109
1110        if(correlationName == null)
1111           toCompare = tableName;
1112        else {
1113            if(allTableName != null)
1114                toCompare = makeTableName(allTableName.getSchemaName(),correlationName);
1115            else
1116                toCompare = makeTableName(null,correlationName);
1117        }
1118
1119        if ( allTableName != null &&
1120             ! allTableName.equals(toCompare))
1121        {
1122            return null;
1123        }
1124
1125        /* Cache exposed name for this table.
1126         * The exposed name becomes the qualifier for each column
1127         * in the expanded list.
1128         */

1129        if (correlationName == null)
1130        {
1131            exposedName = tableName;
1132        }
1133        else
1134        {
1135            exposedName = makeTableName(null, correlationName);
1136        }
1137
1138        rcList = (ResultColumnList) getNodeFactory().getNode(
1139                                        C_NodeTypes.RESULT_COLUMN_LIST,
1140                                        getContextManager());
1141
1142        /* Build a new result column list based off of resultColumns.
1143         * NOTE: This method will capture any column renaming due to
1144         * a derived column list.
1145         */

1146        int inputSize = inputRcl.size();
1147        for (int index = 0; index < inputSize; index++)
1148        {
1149            // Build a ResultColumn/ColumnReference pair for the column //
1150
columnName = ((ResultColumn) inputRcl.elementAt(index)).getName();
1151            valueNode = (ValueNode) getNodeFactory().getNode(
1152                                            C_NodeTypes.COLUMN_REFERENCE,
1153                                            columnName,
1154                                            exposedName,
1155                                            getContextManager());
1156            resultColumn = (ResultColumn) getNodeFactory().getNode(
1157                                            C_NodeTypes.RESULT_COLUMN,
1158                                            columnName,
1159                                            valueNode,
1160                                            getContextManager());
1161
1162            // Build the ResultColumnList to return //
1163
rcList.addResultColumn(resultColumn);
1164        }
1165        return rcList;
1166    }
1167
1168    /**
1169     * Push expressions down to the first ResultSetNode which can do expression
1170     * evaluation and has the same referenced table map.
1171     * RESOLVE - This means only pushing down single table expressions to
1172     * ProjectRestrictNodes today. Once we have a better understanding of how
1173     * the optimizer will work, we can push down join clauses.
1174     *
1175     * @param predicateList The PredicateList.
1176     *
1177     * @exception StandardException Thrown on error
1178     */

1179    void pushExpressions(PredicateList predicateList)
1180                        throws StandardException
1181    {
1182        if (SanityManager.DEBUG)
1183        {
1184            SanityManager.ASSERT(predicateList != null,
1185                             "predicateList is expected to be non-null");
1186        }
1187    }
1188
1189    /**
1190     * Get the exposed name for this table, which is the name that can
1191     * be used to refer to it in the rest of the query.
1192     *
1193     * @return The exposed name of this table.
1194     *
1195     * @exception StandardException Thrown on error
1196     */

1197    public String JavaDoc getExposedName() throws StandardException
1198    {
1199        if (SanityManager.DEBUG)
1200        SanityManager.THROWASSERT(
1201                             "getExposedName() not expected to be called for " + this.getClass().getName());
1202        return null;
1203    }
1204
1205    /**
1206     * Set the table # for this table.
1207     *
1208     * @param tableNumber The table # for this table.
1209     */

1210    public void setTableNumber(int tableNumber)
1211    {
1212        /* This should only be called if the tableNumber has not been set yet */
1213        if (SanityManager.DEBUG)
1214        SanityManager.ASSERT(this.tableNumber == -1,
1215                             "tableNumber is not expected to be already set");
1216        this.tableNumber = tableNumber;
1217    }
1218
1219    /**
1220     * Return a TableName node representing this FromTable.
1221     * Expect this to be overridden (and used) by subclasses
1222     * that may set correlationName to null.
1223     *
1224     * @return a TableName node representing this FromTable.
1225     * @exception StandardException Thrown on error
1226     */

1227    public TableName getTableName()
1228        throws StandardException
1229    {
1230        if (correlationName == null) return null;
1231
1232        if (corrTableName == null)
1233        {
1234            corrTableName = makeTableName(null, correlationName);
1235        }
1236
1237        return corrTableName;
1238    }
1239
1240    /**
1241     * Set the (query block) level (0-based) for this FromTable.
1242     *
1243     * @param level The query block level for this FromTable.
1244     */

1245    public void setLevel(int level)
1246    {
1247        this.level = level;
1248    }
1249
1250    /**
1251     * Get the (query block) level (0-based) for this FromTable.
1252     *
1253     * @return int The query block level for this FromTable.
1254     */

1255    public int getLevel()
1256    {
1257        return level;
1258    }
1259
1260    /**
1261     * Decrement (query block) level (0-based) for this FromTable.
1262     * This is useful when flattening a subquery.
1263     *
1264     * @param decrement The amount to decrement by.
1265     */

1266    void decrementLevel(int decrement)
1267    {
1268        if (SanityManager.DEBUG)
1269        {
1270            /* NOTE: level doesn't get propagated
1271             * to nodes generated after binding.
1272             */

1273            if (level < decrement && level != 0)
1274            {
1275                SanityManager.THROWASSERT(
1276                    "level (" + level +
1277                    ") expected to be >= decrement (" +
1278                    decrement + ")");
1279            }
1280        }
1281        /* NOTE: level doesn't get propagated
1282         * to nodes generated after binding.
1283         */

1284        if (level > 0)
1285        {
1286            level -= decrement;
1287        }
1288    }
1289
1290    /**
1291    * Get a schema descriptor for the given table.
1292    * Uses this.corrTableName.
1293    *
1294    * @return Schema Descriptor
1295    *
1296    * @exception StandardException throws on schema name
1297    * that doesn't exist
1298    */

1299    public SchemaDescriptor getSchemaDescriptor() throws StandardException
1300    {
1301        return getSchemaDescriptor(corrTableName);
1302    }
1303
1304    /**
1305    * Get a schema descriptor for the given table.
1306    *
1307    * @param tableName the table name
1308    *
1309    * @return Schema Descriptor
1310    *
1311    * @exception StandardException throws on schema name
1312    * that doesn't exist
1313    */

1314    public SchemaDescriptor getSchemaDescriptor(TableName tableName) throws StandardException
1315    {
1316        SchemaDescriptor sd;
1317
1318        sd = getSchemaDescriptor(tableName.getSchemaName());
1319
1320        return sd;
1321    }
1322
1323    /**
1324     * Determine whether or not the specified name is an exposed name in
1325     * the current query block.
1326     *
1327     * @param name The specified name to search for as an exposed name.
1328     * @param schemaName Schema name, if non-null.
1329     * @param exactMatch Whether or not we need an exact match on specified schema and table
1330     * names or match on table id.
1331     *
1332     * @return The FromTable, if any, with the exposed name.
1333     *
1334     * @exception StandardException Thrown on error
1335     */

1336    protected FromTable getFromTableByName(String JavaDoc name, String JavaDoc schemaName, boolean exactMatch)
1337        throws StandardException
1338    {
1339        // Only FromBaseTables have schema names
1340
if (schemaName != null)
1341        {
1342            return null;
1343        }
1344
1345        if (getExposedName().equals(name))
1346        {
1347            return this;
1348        }
1349        return null;
1350    }
1351
1352    /**
1353     * Is this FromTable a JoinNode which can be flattened into
1354     * the parents FromList.
1355     *
1356     * @return boolean Whether or not this FromTable can be flattened.
1357     */

1358    public boolean isFlattenableJoinNode()
1359    {
1360        return false;
1361    }
1362
1363    /**
1364     * no LOJ reordering for this FromTable.
1365     */

1366    public boolean LOJ_reorderable(int numTables)
1367        throws StandardException
1368    {
1369        return false;
1370    }
1371
1372    /**
1373     * Transform any Outer Join into an Inner Join where applicable.
1374     * (Based on the existence of a null intolerant
1375     * predicate on the inner table.)
1376     *
1377     * @param predicateTree The predicate tree for the query block
1378     *
1379     * @return The new tree top (OuterJoin or InnerJoin).
1380     *
1381     * @exception StandardException Thrown on error
1382     */

1383    public FromTable transformOuterJoins(ValueNode predicateTree, int numTables)
1384        throws StandardException
1385    {
1386        return this;
1387    }
1388
1389    /**
1390     * Fill the referencedTableMap with this ResultSetNode.
1391     *
1392     * @param passedMap The table map to fill in.
1393     */

1394    public void fillInReferencedTableMap(JBitSet passedMap)
1395    {
1396        if (tableNumber != -1)
1397        {
1398            passedMap.set(tableNumber);
1399        }
1400    }
1401
1402    /**
1403     * Mark as updatable all the columns in the result column list of this
1404     * FromBaseTable that match the columns in the given update column list.
1405     * If the list is null, it means all the columns are updatable.
1406     *
1407     * @param updateColumns A Vector representing the columns
1408     * that can be updated.
1409     */

1410    protected void markUpdatableByCursor(Vector JavaDoc updateColumns)
1411    {
1412        resultColumns.markUpdatableByCursor(updateColumns);
1413    }
1414
1415    /**
1416     * Flatten this FromTable into the outer query block. The steps in
1417     * flattening are:
1418     * o Mark all ResultColumns as redundant, so that they are "skipped over"
1419     * at generate().
1420     * o Append the wherePredicates to the outer list.
1421     * o Return the fromList so that the caller will merge the 2 lists
1422     *
1423     * @param rcl The RCL from the outer query
1424     * @param outerPList PredicateList to append wherePredicates to.
1425     * @param sql The SubqueryList from the outer query
1426     * @param gbl The group by list, if any
1427     *
1428     * @return FromList The fromList from the underlying SelectNode.
1429     *
1430     * @exception StandardException Thrown on error
1431     */

1432    public FromList flatten(ResultColumnList rcl,
1433                            PredicateList outerPList,
1434                            SubqueryList sql,
1435                            GroupByList gbl)
1436
1437            throws StandardException
1438    {
1439        if (SanityManager.DEBUG)
1440        {
1441            SanityManager.THROWASSERT(
1442                 "flatten() not expected to be called for " + this);
1443        }
1444        return null;
1445    }
1446
1447    /**
1448     * Optimize any subqueries that haven't been optimized any where
1449     * else. This is useful for a RowResultSetNode as a derived table
1450     * because it doesn't get optimized otherwise.
1451     *
1452     * @exception StandardException Thrown on error
1453     */

1454    void optimizeSubqueries(DataDictionary dd, double rowCount)
1455        throws StandardException
1456    {
1457    }
1458
1459    /**
1460     * Tell the given RowOrdering about any columns that are constant
1461     * due to their being equality comparisons with constant expressions.
1462     */

1463    protected void tellRowOrderingAboutConstantColumns(
1464                                        RowOrdering rowOrdering,
1465                                        OptimizablePredicateList predList)
1466    {
1467        /*
1468        ** Tell the RowOrdering about columns that are equal to constant
1469        ** expressions.
1470        */

1471        if (predList != null)
1472        {
1473            for (int i = 0; i < predList.size(); i++)
1474            {
1475                Predicate pred = (Predicate) predList.getOptPredicate(i);
1476
1477                /* Is it an = comparison with a constant expression? */
1478                if (pred.equalsComparisonWithConstantExpression(this))
1479                {
1480                    /* Get the column being compared to the constant */
1481                    ColumnReference cr = pred.getRelop().getColumnOperand(this);
1482
1483                    if (cr != null)
1484                    {
1485                        /* Tell RowOrdering that the column is always ordered */
1486                        rowOrdering.columnAlwaysOrdered(this, cr.getColumnNumber());
1487                    }
1488                }
1489            }
1490        }
1491        
1492    }
1493    
1494    public boolean needsSpecialRCLBinding()
1495    {
1496        return false;
1497    }
1498    
1499    /**
1500     * Sets the original or unbound table name for this FromTable.
1501     *
1502     * @param tableName the unbound table name
1503     *
1504     */

1505    public void setOrigTableName(TableName tableName)
1506    {
1507        this.origTableName = tableName;
1508    }
1509    
1510    /**
1511     * Gets the original or unbound table name for this FromTable.
1512     * The tableName field can be changed due to synonym resolution.
1513     * Use this method to retrieve the actual unbound tablename.
1514     *
1515     * @return TableName the original or unbound tablename
1516     *
1517     */

1518    public TableName getOrigTableName()
1519    {
1520        return this.origTableName;
1521    }
1522}
1523
Popular Tags