Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/derby/impl/sql/compile/FromBaseTable.java


1   /*
2   
3      Derby - Class org.apache.derby.impl.sql.compile.FromBaseTable
4   
5      Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable.
6   
7      Licensed under the Apache License, Version 2.0 (the "License");
8      you may not use this file except in compliance with the License.
9      You may obtain a copy of the License at
10  
11        http://www.apache.org/licenses/LICENSE-2.0
12  
13     Unless required by applicable law or agreed to in writing, software
14     distributed under the License is distributed on an "AS IS" BASIS,
15     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16     See the License for the specific language governing permissions and
17     limitations under the License.
18  
19   */
20  
21  package  org.apache.derby.impl.sql.compile;
22  
23  import org.apache.derby.catalog.IndexDescriptor;
24  import org.apache.derby.iapi.util.StringUtil;
25  
26  import org.apache.derby.iapi.reference.ClassName;
27  import org.apache.derby.iapi.reference.SQLState;
28  
29  import org.apache.derby.iapi.services.io.FormatableBitSet;
30  import org.apache.derby.iapi.services.io.FormatableArrayHolder;
31  import org.apache.derby.iapi.services.io.FormatableIntHolder;
32  import org.apache.derby.iapi.util.JBitSet;
33  import org.apache.derby.iapi.util.ReuseFactory;
34  import org.apache.derby.iapi.services.classfile.VMOpcode;
35  
36  import org.apache.derby.iapi.services.loader.GeneratedMethod;
37  import org.apache.derby.iapi.services.context.ContextManager;
38  import org.apache.derby.iapi.services.compiler.MethodBuilder;
39  import org.apache.derby.iapi.services.monitor.Monitor;
40  import org.apache.derby.iapi.services.property.PropertyUtil;
41  import org.apache.derby.iapi.services.sanity.SanityManager;
42  
43  import org.apache.derby.iapi.error.StandardException;
44  
45  import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
46  
47  import org.apache.derby.iapi.sql.compile.CompilerContext;
48  import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
49  import org.apache.derby.iapi.sql.compile.Optimizer;
50  import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
51  import org.apache.derby.iapi.sql.compile.Optimizable;
52  import org.apache.derby.iapi.sql.compile.CostEstimate;
53  import org.apache.derby.iapi.sql.compile.AccessPath;
54  import org.apache.derby.iapi.sql.compile.JoinStrategy;
55  import org.apache.derby.iapi.sql.compile.RowOrdering;
56  import org.apache.derby.iapi.sql.compile.C_NodeTypes;
57  
58  import org.apache.derby.iapi.sql.dictionary.DataDictionary;
59  import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
60  import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
61  import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
62  import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
63  import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
64  import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
65  import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
66  import org.apache.derby.iapi.sql.dictionary.ViewDescriptor;
67  
68  import org.apache.derby.iapi.sql.execute.CursorResultSet;
69  import org.apache.derby.iapi.sql.execute.ExecRow;
70  import org.apache.derby.iapi.sql.execute.ExecutionContext;
71  
72  
73  import org.apache.derby.iapi.sql.ResultSet;
74  import org.apache.derby.iapi.sql.Activation;
75  import org.apache.derby.iapi.sql.LanguageProperties;
76  
77  import org.apache.derby.iapi.types.TypeId;
78  
79  import org.apache.derby.iapi.store.access.Qualifier;
80  import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
81  import org.apache.derby.iapi.store.access.StoreCostController;
82  import org.apache.derby.iapi.store.access.ScanController;
83  import org.apache.derby.iapi.store.access.TransactionController;
84  
85  import org.apache.derby.iapi.types.DataValueDescriptor;
86  
87  import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
88  import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
89  
90  import org.apache.derby.impl.sql.execute.HashScanResultSet;
91  
92  
93  
94  import java.sql.Connection;
95  
96  import java.util.Enumeration;
97  import java.util.Properties;
98  import java.util.Vector;
99  
100 /**
101  * A FromBaseTable represents a table in the FROM list of a DML statement,
102  * as distinguished from a FromSubquery, which represents a subquery in the
103  * FROM list. A FromBaseTable may actually represent a view.  During parsing,
104  * we can't distinguish views from base tables. During binding, when we
105  * find FromBaseTables that represent views, we replace them with FromSubqueries.
106  * By the time we get to code generation, all FromSubqueries have been eliminated,
107  * and all FromBaseTables will represent only true base tables.
108  * <p>
109  * <B>Positioned Update</B>: Currently, all columns of an updatable cursor
110  * are selected to deal with a positioned update.  This is because we don't
111  * know what columns will ultimately be needed from the UpdateNode above
112  * us.  For example, consider:<pre><i>
113  *
114  *   get c as 'select cint from t for update of ctinyint'
115  *  update t set ctinyint = csmallint
116  *
117  * </pre></i> Ideally, the cursor only selects cint.  Then,
118  * something akin to an IndexRowToBaseRow is generated to
119  * take the CursorResultSet and get the appropriate columns
120  * out of the base table from the RowLocation retunrned by the
121  * cursor.  Then the update node can generate the appropriate
122  * NormalizeResultSet (or whatever else it might need) to
123  * get things into the correct format for the UpdateResultSet.
124  * See CurrentOfNode for more information.
125  *
126 * @author Jeff Lichtman
127  */
128 
129 public class FromBaseTable extends FromTable
130 {
131   static final int UNSET = -1;
132 
133   TableName    tableName;
134   TableDescriptor  tableDescriptor;
135 
136   ConglomerateDescriptor    baseConglomerateDescriptor;
137   ConglomerateDescriptor[]  conglomDescs;
138 
139   int        updateOrDelete;
140   
141   /*
142   ** The number of rows to bulkFetch.
143   ** Initially it is unset.  If the user
144   ** uses the bulkFetch table property,  
145   ** it is set to that.  Otherwise, it
146   ** may be turned on if it isn't an updatable
147   ** cursor and it is the right type of
148   ** result set (more than 1 row expected to
149   ** be returned, and not hash, which does its
150   ** own bulk fetch, and subquery).
151   */
152   int       bulkFetch = UNSET;
153 
154   /* We may turn off bulk fetch for a variety of reasons,
155    * including because of the min optimization.  
156    * bulkFetchTurnedOff is set to true in those cases.
157    */
158   boolean      bulkFetchTurnedOff;
159   
160   private double  singleScanRowCount;
161 
162   private FormatableBitSet referencedCols;
163   private ResultColumnList templateColumns;
164 
165   /* A 0-based array of column names for this table used
166    * for optimizer trace.
167    */
168   private String[] columnNames;
169 
170   // true if we are to do a special scan to retrieve the last value
171   // in the index
172   private boolean specialMaxScan;
173 
174   // true if we are to do a distinct scan
175   private boolean distinctScan;
176 
177   /**
178    *Information for dependent table scan for Referential Actions
179    */
180   private boolean raDependentScan;
181   private String raParentResultSetId;
182   private long fkIndexConglomId;  
183   private int[] fkColArray;
184 
185   /**
186    * Restriction as a PredicateList
187    */
188   PredicateList baseTableRestrictionList;
189   PredicateList nonBaseTableRestrictionList;
190   PredicateList restrictionList;
191   PredicateList storeRestrictionList;
192   PredicateList nonStoreRestrictionList;
193   PredicateList requalificationRestrictionList;
194 
195   public static final int UPDATE = 1;
196   public static final int DELETE = 2;
197 
198   /* Variables for EXISTS FBTs */
199   private boolean  existsBaseTable;
200   private boolean  isNotExists;  //is a NOT EXISTS base table
201   private JBitSet dependencyMap;
202 
203   private boolean getUpdateLocks;
204 
205   /**
206    * Initializer for a table in a FROM list.
207    *
208    * @param tableName      The name of the table
209    * @param correlationName  The correlation name
210    * @param derivedRCL    The derived column list
211    * @param tableProperties  The Properties list associated with the table.
212    *
213    *  - OR -
214    *
215    * @param tableName      The name of the table
216    * @param correlationName  The correlation name
217    * @param updateOrDelete  Table is being updated/deleted from. 
218    * @param derivedRCL    The derived column list
219    */
220   public void init(
221               Object arg1,
222               Object arg2,
223                 Object arg3,
224               Object arg4)
225   {
226     if (arg3 instanceof Integer)
227     {
228       init(arg2, null);
229       this.tableName = (TableName) arg1;
230       this.updateOrDelete = ((Integer) arg3).intValue();
231       resultColumns = (ResultColumnList) arg4;
232     }
233     else
234     {
235       init(arg2, arg4);
236       this.tableName = (TableName) arg1;
237       resultColumns = (ResultColumnList) arg3;
238     }
239 
240     templateColumns = resultColumns;
241   }
242 
243   /**
244    * no LOJ reordering for base table.
245    */
246   public boolean LOJ_reorderable(int numTables)
247         throws StandardException
248   {
249     return false;
250   }
251 
252   public JBitSet LOJgetReferencedTables(int numTables)
253         throws StandardException
254   {
255     JBitSet map = new JBitSet(numTables);
256     fillInReferencedTableMap(map);
257     return map;
258   }
259 
260   /*
261    * Optimizable interface.
262    */
263 
264   /**
265    * @see Optimizable#nextAccessPath
266    *
267    * @exception StandardException    Thrown on error
268    */
269   public boolean nextAccessPath(Optimizer optimizer,
270                   OptimizablePredicateList predList,
271                   RowOrdering rowOrdering)
272           throws StandardException
273   {
274     String userSpecifiedIndexName = getUserSpecifiedIndexName();
275     AccessPath ap = getCurrentAccessPath();
276     ConglomerateDescriptor currentConglomerateDescriptor =
277                         ap.getConglomerateDescriptor();
278 
279     optimizer.trace(Optimizer.CALLING_NEXT_ACCESS_PATH,
280              ((predList == null) ? 0 : predList.size()),
281              0, 0.0, getExposedName());
282 
283     /*
284     ** Remove the ordering of the current conglomerate descriptor,
285     ** if any.
286     */
287     rowOrdering.removeOptimizable(getTableNumber());
288 
289     // RESOLVE: This will have to be modified to step through the
290     // join strategies as well as the conglomerates.
291 
292     if (userSpecifiedIndexName != null)
293     {
294       /*
295       ** User specified an index name, so we should look at only one
296       ** index.  If there is a current conglomerate descriptor, and there
297       ** are no more join strategies, we've already looked at the index,
298       ** so go back to null.
299       */
300       if (currentConglomerateDescriptor != null)
301       {
302         if ( ! super.nextAccessPath(optimizer,
303                       predList,
304                       rowOrdering) )
305         {
306           currentConglomerateDescriptor = null;
307         }
308       }
309       else
310       {
311         optimizer.trace(Optimizer.LOOKING_FOR_SPECIFIED_INDEX,
312                 tableNumber, 0, 0.0, userSpecifiedIndexName);
313 
314         if (StringUtil.SQLToUpperCase(userSpecifiedIndexName).equals("NULL"))
315         {
316           /* Special case - user-specified table scan */
317           currentConglomerateDescriptor =
318             tableDescriptor.getConglomerateDescriptor(
319                     tableDescriptor.getHeapConglomerateId()
320                   );
321         }
322         else
323         {
324           /* User-specified index name */
325           getConglomDescs();
326         
327           for (int index = 0; index < conglomDescs.length; index++)
328           {
329             currentConglomerateDescriptor = conglomDescs[index];
330             String conglomerateName =
331               currentConglomerateDescriptor.getConglomerateName();
332             if (conglomerateName != null)
333             {
334               /* Have we found the desired index? */
335               if (conglomerateName.equals(userSpecifiedIndexName))
336               {
337                 break;
338               }
339             }
340           }
341 
342           /* We should always find a match */
343           if (SanityManager.DEBUG)
344           {
345             if (currentConglomerateDescriptor == null)
346             {
347               SanityManager.THROWASSERT(
348                 "Expected to find match for forced index " +
349                 userSpecifiedIndexName);
350             }
351           }
352         }
353 
354         if ( ! super.nextAccessPath(optimizer,
355                       predList,
356                       rowOrdering))
357         {
358           if (SanityManager.DEBUG)
359           {
360             SanityManager.THROWASSERT("No join strategy found");
361           }
362         }
363       }
364     }
365     else
366     {
367       if (currentConglomerateDescriptor != null)
368       {
369         /* 
370         ** Once we have a conglomerate descriptor, cycle through
371         ** the join strategies (done in parent).
372         */
373         if ( ! super.nextAccessPath(optimizer,
374                       predList,
375                       rowOrdering))
376         {
377           /*
378           ** When we're out of join strategies, go to the next
379           ** conglomerate descriptor.
380           */
381           currentConglomerateDescriptor = getNextConglom(currentConglomerateDescriptor);
382 
383           /*
384           ** New conglomerate, so step through join strategies
385           ** again.
386           */
387           resetJoinStrategies(optimizer);
388 
389           if ( ! super.nextAccessPath(optimizer,
390                         predList,
391                         rowOrdering))
392           {
393             if (SanityManager.DEBUG)
394             {
395               SanityManager.THROWASSERT("No join strategy found");
396             }
397           }
398         }
399       }
400       else
401       {
402         /* Get the first conglomerate descriptor */
403         currentConglomerateDescriptor = getFirstConglom();
404 
405         if ( ! super.nextAccessPath(optimizer,
406                       predList,
407                       rowOrdering))
408         {
409           if (SanityManager.DEBUG)
410           {
411             SanityManager.THROWASSERT("No join strategy found");
412           }
413         }
414       }
415     }
416 
417     if (currentConglomerateDescriptor == null)
418     {
419       optimizer.trace(Optimizer.NO_MORE_CONGLOMERATES, tableNumber, 0, 0.0, null);
420     }
421     else
422     {
423       currentConglomerateDescriptor.setColumnNames(columnNames);
424       optimizer.trace(Optimizer.CONSIDERING_CONGLOMERATE, tableNumber, 0, 0.0, 
425               currentConglomerateDescriptor);
426     }
427 
428     /*
429     ** Tell the rowOrdering that what the ordering of this conglomerate is
430     */
431     if (currentConglomerateDescriptor != null)
432     {
433       if ( ! currentConglomerateDescriptor.isIndex())
434       {
435         /* If we are scanning the heap, but there
436          * is a full match on a unique key, then
437          * we can say that the table IS NOT unordered.
438          * (We can't currently say what the ordering is
439          * though.)
440          */
441         if (! isOneRowResultSet(predList))
442         {
443           optimizer.trace(Optimizer.ADDING_UNORDERED_OPTIMIZABLE,
444                    ((predList == null) ? 0 : predList.size()), 
445                    0, 0.0, null);
446 
447           rowOrdering.addUnorderedOptimizable(this);
448         }
449         else
450         {
451           optimizer.trace(Optimizer.SCANNING_HEAP_FULL_MATCH_ON_UNIQUE_KEY,
452                    0, 0, 0.0, null);
453         }
454       }
455       else
456       {
457         IndexRowGenerator irg =
458               currentConglomerateDescriptor.getIndexDescriptor();
459 
460         int[] baseColumnPositions = irg.baseColumnPositions();
461         boolean[] isAscending = irg.isAscending();
462 
463         for (int i = 0; i < baseColumnPositions.length; i++)
464         {
465           /*
466           ** Don't add the column to the ordering if it's already
467           ** an ordered column.  This can happen in the following
468           ** case:
469           **
470           **    create index ti on t(x, y);
471           **    select * from t where x = 1 order by y;
472           **
473           ** Column x is always ordered, so we want to avoid the
474           ** sort when using index ti.  This is accomplished by
475           ** making column y appear as the first ordered column
476           ** in the list.
477           */
478           if ( ! rowOrdering.orderedOnColumn(isAscending[i] ?
479                           RowOrdering.ASCENDING :
480                           RowOrdering.DESCENDING,
481                           getTableNumber(),
482                           baseColumnPositions[i]))
483           {
484             rowOrdering.nextOrderPosition(isAscending[i] ?
485                           RowOrdering.ASCENDING :
486                           RowOrdering.DESCENDING);
487 
488             rowOrdering.addOrderedColumn(isAscending[i] ?
489                           RowOrdering.ASCENDING :
490                           RowOrdering.DESCENDING,
491                           getTableNumber(),
492                           baseColumnPositions[i]);
493           }
494         }
495       }  
496     }
497 
498     ap.setConglomerateDescriptor(currentConglomerateDescriptor);
499 
500     return currentConglomerateDescriptor != null;
501   }
502 
503   /** Tell super-class that this Optimizable can be ordered */
504   protected boolean canBeOrdered()
505   {
506     return true;
507   }
508 
509   /**
510    * @see org.apache.derby.iapi.sql.compile.Optimizable#optimizeIt
511    *
512    * @exception StandardException    Thrown on error
513    */
514   public CostEstimate optimizeIt(
515         Optimizer optimizer,
516         OptimizablePredicateList predList,
517         CostEstimate outerCost,
518         RowOrdering rowOrdering)
519       throws StandardException
520   {
521     optimizer.costOptimizable(
522               this,
523               tableDescriptor,
524               getCurrentAccessPath().getConglomerateDescriptor(),
525               predList,
526               outerCost);
527 
528     return costEstimate;
529   }
530 
531   /** @see Optimizable#getTableDescriptor */
532   public TableDescriptor getTableDescriptor()
533   {
534     return tableDescriptor;
535   }
536 
537 
538   /** @see Optimizable#isMaterializable 
539    *
540    * @exception StandardException    Thrown on error
541    */
542   public boolean isMaterializable()
543     throws StandardException
544   {
545     /* base tables are always materializable */
546     return true;
547   }
548 
549 
550   /**
551    * @see Optimizable#pushOptPredicate
552    *
553    * @exception StandardException    Thrown on error
554    */
555 
556   public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate)
557     throws StandardException
558   {
559     if (SanityManager.DEBUG)
560     {
561       SanityManager.ASSERT(optimizablePredicate instanceof Predicate,
562         "optimizablePredicate expected to be instanceof Predicate");
563     }
564 
565     /* Add the matching predicate to the restrictionList */
566     restrictionList.addPredicate((Predicate) optimizablePredicate);
567 
568     return true;
569   }
570 
571   /**
572    * @see Optimizable#pullOptPredicates
573    *
574    * @exception StandardException    Thrown on error
575    */
576   public void pullOptPredicates(
577                 OptimizablePredicateList optimizablePredicates)
578           throws StandardException
579   {
580     for (int i = restrictionList.size() - 1; i >= 0; i--) {
581       optimizablePredicates.addOptPredicate(
582                   restrictionList.getOptPredicate(i));
583       restrictionList.removeOptPredicate(i);
584     }
585   }
586 
587   /** 
588    * @see Optimizable#isCoveringIndex
589    * @exception StandardException    Thrown on error
590    */
591   public boolean isCoveringIndex(ConglomerateDescriptor cd) throws StandardException
592   {
593     boolean coveringIndex = true;
594     IndexRowGenerator  irg;
595     int[]        baseCols;
596     int          colPos;
597 
598     /* You can only be a covering index if you're an index */
599     if ( ! cd.isIndex())
600       return false;
601 
602     irg = cd.getIndexDescriptor();
603     baseCols = irg.baseColumnPositions();
604 
605     /* First we check to see if this is a covering index */
606     int rclSize = resultColumns.size();
607     for (int index = 0; index < rclSize; index++)
608     {
609       ResultColumn rc = (ResultColumn) resultColumns.elementAt(index);
610 
611       /* Ignore unreferenced columns */
612       if (! rc.isReferenced())
613       {
614         continue;
615       }
616 
617       /* Ignore constants - this can happen if all of the columns
618        * were projected out and we ended up just generating
619        * a "1" in RCL.doProject().
620        */
621       if (rc.getExpression() instanceof ConstantNode)
622       {
623         continue;
624       }
625 
626       coveringIndex = false;
627 
628       colPos = rc.getColumnPosition();
629 
630       /* Is this column in the index? */
631       for (int i = 0; i < baseCols.length; i++)
632       {
633         if (colPos == baseCols[i])
634         {
635           coveringIndex = true;
636           break;
637         }
638       }
639 
640       /* No need to continue if the column was not in the index */
641       if (! coveringIndex)
642       {
643         break;
644       }
645     }
646     return coveringIndex;
647   }
648 
649   /** @see Optimizable#verifyProperties 
650    * @exception StandardException    Thrown on error
651    */
652   public void verifyProperties(DataDictionary dDictionary)
653     throws StandardException
654   {
655     if (tableProperties == null)
656     {
657       return;
658     }
659     /* Check here for:
660      *    invalid properties key
661      *    index and constraint properties
662      *    non-existent index
663      *    non-existent constraint
664      *    invalid joinStrategy
665      *    invalid value for hashInitialCapacity
666      *    invalid value for hashLoadFactor
667      *    invalid value for hashMaxCapacity
668      */
669     boolean indexSpecified = false;
670     boolean constraintSpecified = false;
671     ConstraintDescriptor consDesc = null;
672     Enumeration e = tableProperties.keys();
673 
674       StringUtil.SQLEqualsIgnoreCase(tableDescriptor.getSchemaName(), 
675                        "SYS");
676     while (e.hasMoreElements())
677     {
678       String key = (String) e.nextElement();
679       String value = (String) tableProperties.get(key);
680 
681       if (key.equals("index"))
682       {
683         // User only allowed to specify 1 of index and constraint, not both
684         if (constraintSpecified)
685         {
686           throw StandardException.newException(SQLState.LANG_BOTH_FORCE_INDEX_AND_CONSTRAINT_SPECIFIED, 
687                 getBaseTableName());
688         }
689         indexSpecified = true;
690 
691         /* Validate index name - NULL means table scan */
692         if (! StringUtil.SQLToUpperCase(value).equals("NULL"))
693         {
694           ConglomerateDescriptor cd = null;
695           ConglomerateDescriptor[] cds = tableDescriptor.getConglomerateDescriptors();
696 
697           for (int index = 0; index < cds.length; index++)
698           {
699             cd = cds[index];
700             String conglomerateName = cd.getConglomerateName();
701             if (conglomerateName != null)
702             {
703               if (conglomerateName.equals(value))
704               {
705                 break;
706               }
707             }
708             // Not a match, clear cd
709             cd = null;
710           }
711 
712           // Throw exception if user specified index not found
713           if (cd == null)
714           {
715             throw StandardException.newException(SQLState.LANG_INVALID_FORCED_INDEX1, 
716                     value, getBaseTableName());
717           }
718           /* Query is dependent on the ConglomerateDescriptor */
719           getCompilerContext().createDependency(cd);
720         }
721       }
722       else if (key.equals("constraint"))
723       {
724         // User only allowed to specify 1 of index and constraint, not both
725         if (indexSpecified)
726         {
727           throw StandardException.newException(SQLState.LANG_BOTH_FORCE_INDEX_AND_CONSTRAINT_SPECIFIED, 
728                 getBaseTableName());
729         }
730         constraintSpecified = true;
731 
732         /* Validate constraint name - NULL means table scan */
733         if (! StringUtil.SQLToUpperCase(value).equals("NULL"))
734         {
735           consDesc = 
736             dDictionary.getConstraintDescriptorByName(
737                   tableDescriptor, (SchemaDescriptor)null, value,
738                   false);
739 
740           /* Throw exception if user specified constraint not found
741            * or if it does not have a backing index.
742            */
743           if ((consDesc == null) || ! consDesc.hasBackingIndex())
744           {
745             throw StandardException.newException(SQLState.LANG_INVALID_FORCED_INDEX2, 
746                     value, getBaseTableName());
747           }
748 
749           /* Query is dependent on the ConstraintDescriptor */
750           getCompilerContext().createDependency(consDesc);
751         }
752       }
753       else if (key.equals("joinStrategy"))
754       {
755         userSpecifiedJoinStrategy = StringUtil.SQLToUpperCase(value);
756       }
757       else if (key.equals("hashInitialCapacity"))
758       {
759         initialCapacity = getIntProperty(value, key);
760 
761         // verify that the specified value is valid
762         if (initialCapacity <= 0)
763         {
764           throw StandardException.newException(SQLState.LANG_INVALID_HASH_INITIAL_CAPACITY, 
765               String.valueOf(initialCapacity));
766         }
767       }
768       else if (key.equals("hashLoadFactor"))
769       {
770         try
771         {
772           loadFactor = Float.valueOf(value).floatValue();
773         }
774         catch (NumberFormatException nfe)
775         {
776           throw StandardException.newException(SQLState.LANG_INVALID_NUMBER_FORMAT_FOR_OVERRIDE, 
777               value, key);
778         }
779 
780         // verify that the specified value is valid
781         if (loadFactor <= 0.0 || loadFactor > 1.0)
782         {
783           throw StandardException.newException(SQLState.LANG_INVALID_HASH_LOAD_FACTOR, 
784               value);
785         }
786       }
787       else if (key.equals("hashMaxCapacity"))
788       {
789         maxCapacity = getIntProperty(value, key);
790 
791         // verify that the specified value is valid
792         if (maxCapacity <= 0)
793         {
794           throw StandardException.newException(SQLState.LANG_INVALID_HASH_MAX_CAPACITY, 
795               String.valueOf(maxCapacity));
796         }
797       }
798       else if (key.equals("bulkFetch"))
799       {
800         bulkFetch = getIntProperty(value, key);
801 
802         // verify that the specified value is valid
803         if (bulkFetch <= 0)
804         {
805           throw StandardException.newException(SQLState.LANG_INVALID_BULK_FETCH_VALUE, 
806               String.valueOf(bulkFetch));
807         }
808       
809         // no bulk fetch on updatable scans
810         if (forUpdate())
811         {
812           throw StandardException.newException(SQLState.LANG_INVALID_BULK_FETCH_UPDATEABLE);
813         }
814       }
815       else
816       {
817         // No other "legal" values at this time
818         throw StandardException.newException(SQLState.LANG_INVALID_FROM_TABLE_PROPERTY, key,
819           "index, constraint, joinStrategy");
820       }
821     }
822 
823     /* If user specified a constraint name, then replace it in the 
824      * properties list with the underlying index name to simplify
825      * the code in the optimizer.
826      * NOTE: The code to get from the constraint name, for a constraint
827      * with a backing index, to the index name is convoluted.  Given
828      * the constraint name, we can get the conglomerate id from the
829      * ConstraintDescriptor.  We then use the conglomerate id to get
830      * the ConglomerateDescriptor from the DataDictionary and, finally,
831      * we get the index name (conglomerate name) from the ConglomerateDescriptor.
832      */
833     if (constraintSpecified)
834     {
835       ConglomerateDescriptor cd = 
836         dDictionary.getConglomerateDescriptor(
837           consDesc.getConglomerateId());
838       String indexName = cd.getConglomerateName();
839 
840       tableProperties.remove("constraint");
841       tableProperties.put("index", indexName);
842     }
843   }
844 
845   /** @see Optimizable#getBaseTableName */
846   public String getBaseTableName()
847   {
848     return tableName.getTableName();
849   }
850 
851   /** @see Optimizable#startOptimizing */
852   public void startOptimizing(Optimizer optimizer, RowOrdering rowOrdering)
853   {
854     AccessPath ap = getCurrentAccessPath();
855     AccessPath bestAp = getBestAccessPath();
856     AccessPath bestSortAp = getBestSortAvoidancePath();
857 
858     ap.setConglomerateDescriptor((ConglomerateDescriptor) null);
859     bestAp.setConglomerateDescriptor((ConglomerateDescriptor) null);
860     bestSortAp.setConglomerateDescriptor((ConglomerateDescriptor) null);
861     ap.setCoveringIndexScan(false);
862     bestAp.setCoveringIndexScan(false);
863     bestSortAp.setCoveringIndexScan(false);
864     ap.setLockMode(0);
865     bestAp.setLockMode(0);
866     bestSortAp.setLockMode(0);
867 
868     /*
869     ** Only need to do this for current access path, because the
870     ** costEstimate will be copied to the best access paths as
871     ** necessary.
872     */
873     CostEstimate costEstimate = getCostEstimate(optimizer);
874     ap.setCostEstimate(costEstimate);
875 
876     /*
877     ** This is the initial cost of this optimizable.  Initialize it
878     ** to the maximum cost so that the optimizer will think that
879     ** any access path is better than none.
880     */
881     costEstimate.setCost(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
882 
883     super.startOptimizing(optimizer, rowOrdering);
884   }
885 
886   /** @see Optimizable#convertAbsoluteToRelativeColumnPosition */
887   public int convertAbsoluteToRelativeColumnPosition(int absolutePosition)
888   {
889     return mapAbsoluteToRelativeColumnPosition(absolutePosition);
890   }
891 
892   /**
893    * @see Optimizable#estimateCost
894    *
895    * @exception StandardException    Thrown on error
896    */
897   public CostEstimate estimateCost(OptimizablePredicateList predList,
898                   ConglomerateDescriptor cd,
899                   CostEstimate outerCost,
900                   Optimizer optimizer,
901                   RowOrdering rowOrdering)
902       throws StandardException
903   {
904     double cost;
905     boolean statisticsForTable = false;
906     boolean statisticsForConglomerate = false;
907     /* unknownPredicateList contains all predicates whose effect on
908      * cost/selectivity can't be calculated by the store.
909      */
910     PredicateList unknownPredicateList = null;
911 
912     if (optimizer.useStatistics() && predList != null)
913     {
914       /* if user has specified that we don't use statistics,
915          pretend that statistics don't exist.
916       */
917       statisticsForConglomerate = tableDescriptor.statisticsExist(cd);
918       statisticsForTable = tableDescriptor.statisticsExist(null);
919       unknownPredicateList = new PredicateList();
920       predList.copyPredicatesToOtherList(unknownPredicateList);
921 
922     }
923 
924     AccessPath currentAccessPath = getCurrentAccessPath();
925     JoinStrategy currentJoinStrategy = 
926       currentAccessPath.getJoinStrategy();
927 
928     optimizer.trace(Optimizer.ESTIMATING_COST_OF_CONGLOMERATE,
929             tableNumber, 0, 0.0, cd);
930 
931     /* Get the uniqueness factory for later use (see below) */
932     double tableUniquenessFactor =
933         optimizer.uniqueJoinWithOuterTable(predList);
934 
935     boolean oneRowResultSetForSomeConglom = isOneRowResultSet(predList);
936 
937     /* Get the predicates that can be used for scanning the base table */
938     baseTableRestrictionList.removeAllElements();
939 
940     currentJoinStrategy.getBasePredicates(predList,  
941                      baseTableRestrictionList,
942                      this);
943                   
944     /* RESOLVE: Need to figure out how to cache the StoreCostController */
945     StoreCostController scc = getStoreCostController(cd);
946 
947     CostEstimate costEstimate = getScratchCostEstimate(optimizer);
948 
949     /* First, get the cost for one scan */
950 
951     /* Does the conglomerate match at most one row? */
952     if (isOneRowResultSet(cd, baseTableRestrictionList))
953     {
954       /*
955       ** Tell the RowOrdering that this optimizable is always ordered.
956       ** It will figure out whether it is really always ordered in the
957       ** context of the outer tables and their orderings.
958       */
959       rowOrdering.optimizableAlwaysOrdered(this);
960 
961       singleScanRowCount = 1.0;
962 
963       /* Yes, the cost is to fetch exactly one row */
964       // RESOLVE: NEED TO FIGURE OUT HOW TO GET REFERENCED COLUMN LIST,
965       // FIELD STATES, AND ACCESS TYPE
966       cost = scc.getFetchFromFullKeyCost(
967                     (FormatableBitSet) null,
968                     0);
969 
970       optimizer.trace(Optimizer.MATCH_SINGLE_ROW_COST,
971               tableNumber, 0, cost, null);
972 
973       costEstimate.setCost(cost, 1.0d, 1.0d);
974 
975       /*
976       ** Let the join strategy decide whether the cost of the base
977       ** scan is a single scan, or a scan per outer row.
978       ** NOTE: The multiplication should only be done against the
979       ** total row count, not the singleScanRowCount.
980       */
981       double newCost = costEstimate.getEstimatedCost();
982 
983       if (currentJoinStrategy.multiplyBaseCostByOuterRows())
984       {
985         newCost *= outerCost.rowCount();
986       }
987 
988       costEstimate.setCost(
989         newCost,
990         costEstimate.rowCount() * outerCost.rowCount(),
991         costEstimate.singleScanRowCount());
992 
993       /*
994       ** Choose the lock mode.  If the start/stop conditions are
995       ** constant, choose row locking, because we will always match
996       ** the same row.  If they are not constant (i.e. they include
997       ** a join), we decide whether to do row locking based on
998       ** the total number of rows for the life of the query.
999       */
1000      boolean constantStartStop = true;
1001      for (int i = 0; i < predList.size(); i++)
1002      {
1003        OptimizablePredicate pred = predList.getOptPredicate(i);
1004
1005        /*
1006        ** The predicates are in index order, so the start and
1007        ** stop keys should be first.
1008        */
1009        if ( ! (pred.isStartKey() || pred.isStopKey()))
1010        {
1011          break;
1012        }
1013
1014        /* Stop when we've found a join */
1015        if ( ! pred.getReferencedMap().hasSingleBitSet())
1016        {
1017          constantStartStop = false;
1018          break;
1019        }
1020      }
1021
1022      if (constantStartStop)
1023      {
1024        currentAccessPath.setLockMode(
1025                      TransactionController.MODE_RECORD);
1026
1027        optimizer.trace(Optimizer.ROW_LOCK_ALL_CONSTANT_START_STOP,
1028                0, 0, 0.0, null);
1029      }
1030      else
1031      {
1032        setLockingBasedOnThreshold(optimizer, costEstimate.rowCount());
1033      }
1034
1035      optimizer.trace(Optimizer.COST_OF_N_SCANS, 
1036              tableNumber, 0, outerCost.rowCount(), costEstimate);
1037
1038      /* Add in cost of fetching base row for non-covering index */
1039      if (cd.isIndex() && ( ! isCoveringIndex(cd) ) )
1040      {
1041        double singleFetchCost =
1042            getBaseCostController().getFetchFromRowLocationCost(
1043                                (FormatableBitSet) null,
1044                                0);
1045        cost = singleFetchCost * costEstimate.rowCount();
1046
1047        costEstimate.setEstimatedCost(
1048                costEstimate.getEstimatedCost() + cost);
1049
1050        optimizer.trace(Optimizer.NON_COVERING_INDEX_COST,
1051                tableNumber, 0, cost, null);
1052      }
1053    }
1054    else
1055    {
1056      /* Conglomerate might match more than one row */
1057
1058      /*
1059      ** Some predicates are good for start/stop, but we don't know
1060      ** the values they are being compared to at this time, so we
1061      ** estimate their selectivity in language rather than ask the
1062      ** store about them .  The predicates on the first column of
1063      ** the conglomerate reduce the number of pages and rows scanned.
1064      ** The predicates on columns after the first reduce the number
1065      ** of rows scanned, but have a much smaller effect on the number
1066      ** of pages scanned, so we keep track of these selectivities in
1067      ** two separate variables: extraFirstColumnSelectivity and
1068      ** extraStartStopSelectivity. (Theoretically, we could try to
1069      ** figure out the effect of predicates after the first column
1070      ** on the number of pages scanned, but it's too hard, so we
1071      ** use these predicates only to reduce the estimated number of
1072      ** rows.  For comparisons with known values, though, the store
1073      ** can figure out exactly how many rows and pages are scanned.)
1074      **
1075      ** Other predicates are not good for start/stop.  We keep track
1076      ** of their selectvities separately, because these limit the
1077      ** number of rows, but not the number of pages, and so need to
1078      ** be factored into the row count but not into the cost.
1079      ** These selectivities are factored into extraQualifierSelectivity.
1080      **
1081      ** statStartStopSelectivity (using statistics) represents the 
1082      ** selectivity of start/stop predicates that can be used to scan 
1083      ** the index. If no statistics exist for the conglomerate then 
1084      ** the value of this variable remains at 1.0
1085      ** 
1086      ** statCompositeSelectivity (using statistics) represents the 
1087      ** selectivity of all the predicates (including NonBaseTable 
1088      ** predicates). This represents the most educated guess [among 
1089      ** all the wild surmises in this routine] as to the number
1090      ** of rows that will be returned from this joinNode.
1091      ** If no statistics exist on the table or no statistics at all
1092      ** can be found to satisfy the predicates at this join opertor,
1093      ** then statCompositeSelectivity is left initialized at 1.0
1094      */
1095      double extraFirstColumnSelectivity = 1.0d;
1096      double extraStartStopSelectivity = 1.0d;
1097      double extraQualifierSelectivity = 1.0d;
1098      double extraNonQualifierSelectivity = 1.0d;
1099      double statStartStopSelectivity = 1.0d;
1100      double statCompositeSelectivity = 1.0d;
1101
1102      int     numExtraFirstColumnPreds = 0;
1103      int     numExtraStartStopPreds = 0;
1104      int     numExtraQualifiers = 0;
1105      int     numExtraNonQualifiers = 0;
1106
1107      /*
1108      ** It is possible for something to be a start or stop predicate
1109      ** without it being possible to use it as a key for cost estimation.
1110      ** For example, with an index on (c1, c2), and the predicate
1111      ** c1 = othertable.c3 and c2 = 1, the comparison on c1 is with
1112      ** an unknown value, so we can't pass it to the store.  This means
1113      ** we can't pass the comparison on c2 to the store, either.
1114      **
1115      ** The following booleans keep track of whether we have seen
1116      ** gaps in the keys we can pass to the store.
1117      */
1118      boolean startGap = false;
1119      boolean stopGap = false;
1120      boolean seenFirstColumn = false;
1121
1122      /*
1123      ** We need to figure out the number of rows touched to decide
1124      ** whether to use row locking or table locking.  If the start/stop
1125      ** conditions are constant (i.e. no joins), the number of rows
1126      ** touched is the number of rows per scan.  But if the start/stop
1127      ** conditions contain a join, the number of rows touched must
1128      ** take the number of outer rows into account.
1129      */
1130      boolean constantStartStop = true;
1131      boolean startStopFound = false;
1132
1133      /* Count the number of start and stop keys */
1134      int startKeyNum = 0;
1135      int stopKeyNum = 0;
1136      OptimizablePredicate pred;
1137      int predListSize;
1138
1139      if (predList != null)
1140        predListSize = baseTableRestrictionList.size();
1141      else
1142        predListSize = 0;
1143
1144      int startStopPredCount = 0;
1145      ColumnReference firstColumn = null;
1146      for (int i = 0; i < predListSize; i++)
1147      {
1148        pred = baseTableRestrictionList.getOptPredicate(i);
1149        boolean startKey = pred.isStartKey();
1150        boolean stopKey = pred.isStopKey();
1151        if (startKey || stopKey)
1152        {
1153          startStopFound = true;
1154
1155          if ( ! pred.getReferencedMap().hasSingleBitSet())
1156          {
1157            constantStartStop = false;
1158          }
1159
1160          boolean knownConstant =
1161            pred.compareWithKnownConstant(this, true);
1162          if (startKey)
1163          {
1164            if (knownConstant && ( ! startGap ) )
1165            {
1166              startKeyNum++;
1167                if (unknownPredicateList != null)
1168                  unknownPredicateList.removeOptPredicate(pred);
1169            }
1170            else
1171            {
1172              startGap = true;
1173            }
1174          }
1175
1176          if (stopKey)
1177          {
1178            if (knownConstant && ( ! stopGap ) )
1179            {
1180              stopKeyNum++;
1181                if (unknownPredicateList != null)
1182                  unknownPredicateList.removeOptPredicate(pred);
1183            }
1184            else
1185            {
1186              stopGap = true;
1187            }
1188          }
1189
1190          /* If either we are seeing startGap or stopGap because start/stop key is
1191           * comparison with non-constant, we should multiply the selectivity to
1192           * extraFirstColumnSelectivity.  Beetle 4787.
1193           */
1194          if (startGap || stopGap)
1195          {
1196            // Don't include redundant join predicates in selectivity calculations
1197            if (baseTableRestrictionList.isRedundantPredicate(i))
1198              continue;
1199
1200            if (startKey && stopKey)
1201              startStopPredCount++;
1202
1203            if (pred.getIndexPosition() == 0)
1204            {
1205              extraFirstColumnSelectivity *=
1206                            pred.selectivity(this);
1207              if (! seenFirstColumn)
1208              {
1209                ValueNode relNode = ((Predicate) pred).getAndNode().getLeftOperand();
1210                if (relNode instanceof BinaryRelationalOperatorNode)
1211                  firstColumn = ((BinaryRelationalOperatorNode) relNode).getColumnOperand(this);
1212                seenFirstColumn = true;
1213              }
1214            }
1215            else
1216            {
1217              extraStartStopSelectivity *= pred.selectivity(this);
1218              numExtraStartStopPreds++;
1219            }
1220          }
1221        }
1222        else
1223        {
1224          // Don't include redundant join predicates in selectivity calculations
1225          if (baseTableRestrictionList.isRedundantPredicate(i))
1226          {
1227            continue;
1228          }
1229
1230          /* If we have "like" predicate on the first index column, it is more likely
1231           * to have a smaller range than "between", so we apply extra selectivity 0.2
1232           * here.  beetle 4387, 4787.
1233           */
1234          if (pred instanceof Predicate)
1235          {
1236            ValueNode leftOpnd = ((Predicate) pred).getAndNode().getLeftOperand();
1237            if (firstColumn != null && leftOpnd instanceof LikeEscapeOperatorNode)
1238            {
1239              LikeEscapeOperatorNode likeNode = (LikeEscapeOperatorNode) leftOpnd;
1240              if (likeNode.getLeftOperand().isParameterNode())
1241              {
1242                ValueNode receiver = ((TernaryOperatorNode) likeNode).getReceiver();
1243                if (receiver instanceof ColumnReference)
1244                {
1245                  ColumnReference cr = (ColumnReference) receiver;
1246                  if (cr.getTableNumber() == firstColumn.getTableNumber() &&
1247                    cr.getColumnNumber() == firstColumn.getColumnNumber())
1248                    extraFirstColumnSelectivity *= 0.2;
1249                }
1250              }
1251            }
1252          }
1253
1254          if (pred.isQualifier())
1255          {
1256            extraQualifierSelectivity *= pred.selectivity(this);
1257            numExtraQualifiers++;
1258          }
1259          else
1260          {
1261            extraNonQualifierSelectivity *= pred.selectivity(this);
1262            numExtraNonQualifiers++;
1263          }
1264
1265          /*
1266          ** Strictly speaking, it shouldn't be necessary to
1267          ** indicate a gap here, since there should be no more
1268          ** start/stop predicates, but let's do it, anyway.
1269          */
1270          startGap = true;
1271          stopGap = true;
1272        }
1273      }
1274
1275      if (unknownPredicateList != null)
1276      {
1277        statCompositeSelectivity = unknownPredicateList.selectivity(this);
1278        if (statCompositeSelectivity == -1.0d)
1279          statCompositeSelectivity = 1.0d;
1280      }
1281
1282      if (seenFirstColumn && statisticsForConglomerate &&
1283        (startStopPredCount > 0))
1284      {
1285        statStartStopSelectivity = 
1286          tableDescriptor.selectivityForConglomerate(cd, startStopPredCount);
1287      }
1288
1289      /*
1290      ** Factor the non-base-table predicates into the extra
1291      ** non-qualifier selectivity, since these will restrict the
1292      ** number of rows, but not the cost.
1293      */
1294      extraNonQualifierSelectivity *=
1295        currentJoinStrategy.nonBasePredicateSelectivity(this, predList);
1296
1297      /* Create the start and stop key arrays, and fill them in */
1298      DataValueDescriptor[] startKeys;
1299      DataValueDescriptor[] stopKeys;
1300
1301      if (startKeyNum > 0)
1302        startKeys = new DataValueDescriptor[startKeyNum];
1303      else
1304        startKeys = null;
1305
1306      if (stopKeyNum > 0)
1307        stopKeys = new DataValueDescriptor[stopKeyNum];
1308      else
1309        stopKeys = null;
1310
1311      startKeyNum = 0;
1312      stopKeyNum = 0;
1313      startGap = false;
1314      stopGap = false;
1315
1316      for (int i = 0; i < predListSize; i++)
1317      {
1318        pred = baseTableRestrictionList.getOptPredicate(i);
1319        boolean startKey = pred.isStartKey();
1320        boolean stopKey = pred.isStopKey();
1321
1322        if (startKey || stopKey)
1323        {
1324          boolean knownConstant = pred.compareWithKnownConstant(this, true);
1325
1326          if (startKey)
1327          {
1328            if (knownConstant && ( ! startGap ) )
1329            {
1330              startKeys[startKeyNum] = pred.getCompareValue(this);
1331              startKeyNum++;
1332            }
1333            else
1334            {
1335              startGap = true;
1336            }
1337          }
1338
1339          if (stopKey)
1340          {
1341            if (knownConstant && ( ! stopGap ) )
1342            {
1343              stopKeys[stopKeyNum] = pred.getCompareValue(this);
1344              stopKeyNum++;
1345            }
1346            else
1347            {
1348              stopGap = true;
1349            }
1350          }
1351        }
1352        else
1353        {
1354          startGap = true;
1355          stopGap = true;
1356        }
1357      }
1358
1359      int startOperator;
1360      int stopOperator;
1361
1362      if (baseTableRestrictionList != null)
1363      {
1364        startOperator = baseTableRestrictionList.startOperator(this);
1365        stopOperator = baseTableRestrictionList.stopOperator(this);
1366      }
1367      else
1368      {
1369        /*
1370        ** If we're doing a full scan, it doesn't matter what the
1371        ** start and stop operators are.
1372        */
1373        startOperator = ScanController.NA;
1374        stopOperator = ScanController.NA;
1375      }
1376
1377      /*
1378      ** Get a row template for this conglomerate.  For now, just tell
1379      ** it we are using all the columns in the row.
1380      */
1381      DataValueDescriptor[] rowTemplate = 
1382                getRowTemplate(cd, getBaseCostController());
1383
1384      /* we prefer index than table scan for concurrency reason, by a small
1385       * adjustment on estimated row count.  This affects optimizer's decision
1386       * especially when few rows are in table. beetle 5006. This makes sense
1387       * since the plan may stay long before we actually check and invalidate it.
1388       * And new rows may be inserted before we check and invalidate the plan.
1389       * Here we only prefer index that has start/stop key from predicates. Non-
1390       * constant start/stop key case is taken care of by selectivity later.
1391       */
1392      long baseRC = (startKeys != null || stopKeys != null) ? baseRowCount() : baseRowCount() + 5;
1393
1394      scc.getScanCost(
1395          currentJoinStrategy.scanCostType(),
1396          baseRC,
1397                    1,
1398          forUpdate(),
1399          (FormatableBitSet) null,
1400          rowTemplate,
1401          startKeys,
1402          startOperator,
1403          stopKeys,
1404          stopOperator,
1405          false,
1406          0,
1407          costEstimate);
1408
1409      /* initialPositionCost is the first part of the index scan cost we get above.
1410       * It's the cost of initial positioning/fetch of key.  So it's unrelated to
1411       * row count of how many rows we fetch from index.  We extract it here so that
1412       * we only multiply selectivity to the other part of index scan cost, which is
1413       * nearly linear, to make cost calculation more accurate and fair, especially
1414       * compared to the plan of "one row result set" (unique index). beetle 4787.
1415       */
1416      double initialPositionCost = 0.0;
1417      if (cd.isIndex())
1418      {
1419        initialPositionCost = scc.getFetchFromFullKeyCost((FormatableBitSet) null, 0);
1420        /* oneRowResultSetForSomeConglom means there's a unique index, but certainly
1421         * not this one since we are here.  If store knows this non-unique index
1422         * won't return any row or just returns one row (eg., the predicate is a
1423         * comparison with constant or almost empty table), we do minor adjustment
1424         * on cost (affecting decision for covering index) and rc (decision for
1425         * non-covering). The purpose is favoring unique index. beetle 5006.
1426         */
1427        if (oneRowResultSetForSomeConglom && costEstimate.rowCount() <= 1)
1428        {
1429          costEstimate.setCost(costEstimate.getEstimatedCost() * 2,
1430                     costEstimate.rowCount() + 2,
1431                     costEstimate.singleScanRowCount() + 2);
1432        }
1433      }
1434
1435      optimizer.trace(Optimizer.COST_OF_CONGLOMERATE_SCAN1,
1436              tableNumber, 0, 0.0, cd);
1437      optimizer.trace(Optimizer.COST_OF_CONGLOMERATE_SCAN2,
1438              tableNumber, 0, 0.0, costEstimate);
1439      optimizer.trace(Optimizer.COST_OF_CONGLOMERATE_SCAN3,
1440              numExtraFirstColumnPreds, 0, 
1441              extraFirstColumnSelectivity, null);
1442      optimizer.trace(Optimizer.COST_OF_CONGLOMERATE_SCAN4,
1443              numExtraStartStopPreds, 0, 
1444              extraStartStopSelectivity, null);
1445      optimizer.trace(Optimizer.COST_OF_CONGLOMERATE_SCAN7,
1446