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