KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > execute > TableScanResultSet


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

21
22 package org.apache.derby.impl.sql.execute;
23
24 import org.apache.derby.iapi.sql.execute.CursorResultSet;
25 import org.apache.derby.iapi.error.StandardException;
26 import org.apache.derby.iapi.services.i18n.MessageService;
27
28 import org.apache.derby.iapi.sql.Activation;
29 import org.apache.derby.iapi.sql.ResultSet;
30 import org.apache.derby.iapi.sql.execute.ExecRow;
31 import org.apache.derby.iapi.sql.execute.ExecIndexRow;
32 import org.apache.derby.iapi.sql.execute.ExecutionContext;
33 import org.apache.derby.iapi.sql.execute.NoPutResultSet;
34 import org.apache.derby.iapi.sql.execute.TemporaryRowHolder;
35
36 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
37
38 import org.apache.derby.iapi.store.access.ConglomerateController;
39 import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
40 import org.apache.derby.iapi.store.access.Qualifier;
41 import org.apache.derby.iapi.store.access.ScanController;
42 import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
43 import org.apache.derby.iapi.store.access.TransactionController;
44
45 import org.apache.derby.iapi.types.DataValueDescriptor;
46
47 import org.apache.derby.iapi.types.Orderable;
48 import org.apache.derby.iapi.types.RowLocation;
49
50 import org.apache.derby.iapi.services.sanity.SanityManager;
51
52 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
53 import org.apache.derby.iapi.services.stream.InfoStreams;
54
55 import org.apache.derby.iapi.services.monitor.Monitor;
56
57 import org.apache.derby.iapi.services.loader.GeneratedMethod;
58 import org.apache.derby.iapi.services.io.FormatableBitSet;
59
60 import org.apache.derby.iapi.reference.SQLState;
61
62 import java.util.Properties JavaDoc;
63 import java.util.Hashtable JavaDoc;
64
65 /**
66  * Takes a table and a table filter and returns
67  * the table's rows satisfying the filter as a result set.
68  *
69  * There are several things we could do during object
70  * construction that are done in the open & next calls, to
71  * improve performance.
72  *
73  * @author ames
74  */

75 class TableScanResultSet extends NoPutResultSetImpl
76     implements CursorResultSet, Cloneable JavaDoc
77 {
78     protected ScanController scanController;
79     protected boolean scanControllerOpened;
80     protected boolean isKeyed;
81     protected boolean firstScan = true;
82     protected ExecIndexRow startPosition;
83     protected ExecIndexRow stopPosition;
84     protected ExecRow candidate;
85
86     // set in constructor and not altered during
87
// life of object.
88
protected long conglomId;
89     protected DynamicCompiledOpenConglomInfo dcoci;
90     protected StaticCompiledOpenConglomInfo scoci;
91     protected GeneratedMethod resultRowAllocator;
92     protected GeneratedMethod startKeyGetter;
93     protected int startSearchOperator;
94     protected GeneratedMethod stopKeyGetter;
95     protected int stopSearchOperator;
96     public Qualifier[][] qualifiers;
97     public String JavaDoc tableName;
98     public String JavaDoc userSuppliedOptimizerOverrides;
99     public String JavaDoc indexName;
100     protected boolean runTimeStatisticsOn;
101     protected FormatableBitSet accessedCols;
102     protected int[] indexCols; //index keys base column position array
103
public int rowsPerRead;
104     public boolean forUpdate;
105     private boolean sameStartStopPosition;
106     private boolean nextDone;
107     private RowLocation rlTemplate;
108
109     public int isolationLevel;
110     public int lockMode;
111
112     // Run time statistics
113
private Properties JavaDoc scanProperties;
114     public String JavaDoc startPositionString;
115     public String JavaDoc stopPositionString;
116     public boolean isConstraint;
117     public boolean coarserLock;
118     public boolean oneRowScan;
119
120     protected long rowsThisScan;
121
122     private long estimatedRowCount;
123
124     /* Following fields are used by beetle 3865, updateable cursor using index. "past2FutureTbl"
125      * is a hash table containing updated rows that are thrown into future direction of the
126      * index scan and as a result we'll hit it again but should skip it. If this hash table
127      * is full, we scan forward and have a virtual memory style temp heap holding future row
128      * id's.
129      */

130     protected Hashtable JavaDoc past2FutureTbl;
131     protected TemporaryRowHolder futureForUpdateRows; //tmp table for materialized rids
132
protected TemporaryRowHolderResultSet futureRowResultSet; //result set for reading from above
133
protected boolean skipFutureRowHolder; //skip reading rows from above
134
protected boolean sourceDrained; //all row ids materialized
135
protected boolean currentRowPrescanned; //got a row from above tmp table
136
protected boolean compareToLastKey; //see comments in UpdateResultSet
137
protected ExecRow lastCursorKey;
138     private ExecRow sparseRow; //sparse row in heap column order
139
private FormatableBitSet sparseRowMap; //which columns to read
140

141     // For Scrollable insensitive updatable result sets, only qualify a row the
142
// first time it's been read, since an update can change a row so that it
143
// no longer qualifies
144
private boolean qualify;
145
146     // currentRowIsValid is set to the result of positioning at a rowLocation.
147
// It will be true if the positioning was successful and false if the row
148
// was deleted under our feet. Whenenver currentRowIsValid is false it means
149
// that the row has been deleted.
150
private boolean currentRowIsValid;
151     
152     // Indicates whether the scan has been positioned back to a previously read
153
// row, or it is accessing a row for the first time.
154
private boolean scanRepositioned;
155
156     //
157
// class interface
158
//
159
TableScanResultSet(long conglomId,
160         StaticCompiledOpenConglomInfo scoci,
161         Activation activation,
162         GeneratedMethod resultRowAllocator,
163         int resultSetNumber,
164         GeneratedMethod startKeyGetter, int startSearchOperator,
165         GeneratedMethod stopKeyGetter, int stopSearchOperator,
166         boolean sameStartStopPosition,
167         Qualifier[][] qualifiers,
168         String JavaDoc tableName,
169         String JavaDoc userSuppliedOptimizerOverrides,
170         String JavaDoc indexName,
171         boolean isConstraint,
172         boolean forUpdate,
173         int colRefItem,
174         int indexColItem,
175         int lockMode,
176         boolean tableLocked,
177         int isolationLevel,
178         int rowsPerRead,
179         boolean oneRowScan,
180         double optimizerEstimatedRowCount,
181         double optimizerEstimatedCost)
182             throws StandardException
183     {
184         super(activation,
185                 resultSetNumber,
186                 optimizerEstimatedRowCount,
187                 optimizerEstimatedCost);
188
189         this.conglomId = conglomId;
190
191         /* Static info created at compile time and can be shared across
192          * instances of the plan.
193          * Dynamic info created on 1st opening of this ResultSet as
194          * it cannot be shared.
195          */

196         this.scoci = scoci;
197
198         if (SanityManager.DEBUG) {
199             SanityManager.ASSERT( activation!=null, "table scan must get activation context");
200             SanityManager.ASSERT( resultRowAllocator!= null, "table scan must get row allocator");
201             if (sameStartStopPosition)
202             {
203                 SanityManager.ASSERT(stopKeyGetter == null,
204                     "stopKeyGetter expected to be null when sameStartStopPosition is true");
205             }
206         }
207
208         this.resultRowAllocator = resultRowAllocator;
209
210         this.startKeyGetter = startKeyGetter;
211         this.startSearchOperator = startSearchOperator;
212         this.stopKeyGetter = stopKeyGetter;
213         this.stopSearchOperator = stopSearchOperator;
214         this.sameStartStopPosition = sameStartStopPosition;
215         this.qualifiers = qualifiers;
216         this.tableName = tableName;
217         this.userSuppliedOptimizerOverrides = userSuppliedOptimizerOverrides;
218         this.indexName = indexName;
219         this.isConstraint = isConstraint;
220         this.forUpdate = forUpdate;
221         this.rowsPerRead = rowsPerRead;
222         this.oneRowScan = oneRowScan;
223
224         // retrieve the valid column list from
225
// the saved objects, if it exists
226
this.accessedCols = null;
227         if (colRefItem != -1)
228         {
229             this.accessedCols = (FormatableBitSet)(activation.getPreparedStatement().
230                         getSavedObject(colRefItem));
231         }
232         if (indexColItem != -1)
233         {
234             this.indexCols = (int[])(activation.getPreparedStatement().
235                         getSavedObject(indexColItem));
236         }
237         if (indexCols != null)
238             activation.setForUpdateIndexScan(this);
239
240         this.lockMode = lockMode;
241
242         /* Isolation level - translate from language to store */
243         // If not specified, get current isolation level
244
if (isolationLevel == ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL)
245         {
246             isolationLevel = lcc.getCurrentIsolationLevel();
247         }
248
249         if (isolationLevel == ExecutionContext.SERIALIZABLE_ISOLATION_LEVEL)
250         {
251             this.isolationLevel = TransactionController.ISOLATION_SERIALIZABLE;
252         }
253         else
254         {
255             /* NOTE: always do row locking on READ COMMITTED/UNCOMITTED scans,
256              * unless the table is marked as table locked (in sys.systables)
257              * This is to improve concurrency. Also see FromBaseTable's
258              * updateTargetLockMode (KEEP THESE TWO PLACES CONSISTENT!
259              * bug 4318).
260              */

261
262             /* NOTE: always do row locking on READ COMMITTED/UNCOMMITTED
263              * and repeatable read scans unless the table is marked as
264              * table locked (in sys.systables).
265              *
266              * We always get instantaneous locks as we will complete
267              * the scan before returning any rows and we will fully
268              * requalify the row if we need to go to the heap on a next().
269              */

270
271             if (! tableLocked)
272             {
273                 this.lockMode = TransactionController.MODE_RECORD;
274             }
275
276             if (isolationLevel ==
277                     ExecutionContext.READ_COMMITTED_ISOLATION_LEVEL)
278             {
279                 /*
280                  * Now we see if we can get instantaneous locks
281                  * if we are getting share locks.
282                  * (For example, we can get instantaneous locks
283                  * when doing a bulk fetch.)
284                  */

285                 if ((! forUpdate) && canGetInstantaneousLocks())
286                 {
287                     this.isolationLevel =
288                         TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK;
289                 }
290                 else
291                 {
292                     this.isolationLevel =
293                         TransactionController.ISOLATION_READ_COMMITTED;
294                 }
295             }
296             else if (isolationLevel ==
297                         ExecutionContext.READ_UNCOMMITTED_ISOLATION_LEVEL)
298             {
299                 this.isolationLevel =
300                     TransactionController.ISOLATION_READ_UNCOMMITTED;
301             }
302             else if (isolationLevel ==
303                         ExecutionContext.REPEATABLE_READ_ISOLATION_LEVEL)
304             {
305                 this.isolationLevel =
306                     TransactionController.ISOLATION_REPEATABLE_READ;
307             }
308         }
309
310         if (SanityManager.DEBUG)
311         {
312             SanityManager.ASSERT(
313                 ((isolationLevel ==
314                       ExecutionContext.READ_COMMITTED_ISOLATION_LEVEL) ||
315                  (isolationLevel ==
316                       ExecutionContext.READ_UNCOMMITTED_ISOLATION_LEVEL) ||
317                  (isolationLevel ==
318                       ExecutionContext.REPEATABLE_READ_ISOLATION_LEVEL) ||
319                  (isolationLevel ==
320                       ExecutionContext.SERIALIZABLE_ISOLATION_LEVEL)),
321
322                 "Invalid isolation level - " + isolationLevel);
323         }
324
325         runTimeStatisticsOn = (activation != null &&
326                                activation.getLanguageConnectionContext().getRunTimeStatisticsMode());
327
328         /* Only call row allocators once */
329         candidate = (ExecRow) resultRowAllocator.invoke(activation);
330         constructorTime += getElapsedMillis(beginTime);
331         
332         /* Always qualify the first time a row is being read */
333         qualify = true;
334         currentRowIsValid = false;
335         scanRepositioned = false;
336     }
337
338     //
339
// ResultSet interface (leftover from NoPutResultSet)
340
//
341

342     /**
343      * open a scan on the table. scan parameters are evaluated
344      * at each open, so there is probably some way of altering
345      * their values...
346      *
347      * @exception StandardException thrown on failure to open
348      */

349     public void openCore() throws StandardException
350     {
351         if (SanityManager.DEBUG)
352             SanityManager.ASSERT( ! isOpen, "TableScanResultSet already open");
353
354         // Get the current transaction controller
355
TransactionController tc = activation.getTransactionController();
356
357         if (dcoci == null)
358             dcoci = tc.getDynamicCompiledConglomInfo(conglomId);
359
360
361         if (startKeyGetter != null)
362         {
363             startPosition = (ExecIndexRow) startKeyGetter.invoke(activation);
364             if (sameStartStopPosition)
365             {
366                 stopPosition = startPosition;
367             }
368         }
369         if (stopKeyGetter != null)
370         {
371             stopPosition = (ExecIndexRow) stopKeyGetter.invoke(activation);
372         }
373
374         /* NOTE: We always open the ScanController on the 1st open
375          * to do the keyed conglomerate check.
376          */

377
378         // Determine whether the conglomerate is keyed. This determines
379
// how we find the RowLocation for the base heap. For non-keyed
380
// conglomerates, we ask the scan. For keyed conglomerates, it
381
// is the last column in the row.
382
//
383
// Do this here, rather than in the constructor, so we can avoid
384
// throwing exceptions from the constructor
385
if (firstScan)
386         {
387             openScanController(tc);
388
389             isKeyed = scanController.isKeyed();
390
391             /*
392             ** If scan tracing is turned on, print information about this
393             ** TableScanResultSet when it is first opened. We would like
394             ** to do this when it is constructed, but it is not always
395             ** possible to get the start and stop positioners at the time
396             ** this object is constructed (because they may depend on outer
397             ** rows).
398             */

399             if (SanityManager.DEBUG)
400             {
401                 if (SanityManager.DEBUG_ON("ScanTrace"))
402                 {
403                     //traceScanParameters();
404
}
405             }
406         }
407
408         // Check whether there are any comparisons with unordered nulls
409
// on either the start or stop position. If there are, we can
410
// (and must) skip the scan, because no rows can qualify
411
if (skipScan(startPosition, stopPosition))
412         {
413             scanControllerOpened = false;
414         }
415         /* NOTE: We always open the ScanController on the 1st open
416          * to do the keyed conglomerate check, so we only need to
417          * do it here if not the 1st scan.
418          */

419         else if (! firstScan)
420         {
421             openScanController(tc);
422         }
423
424         /* If the scan is on an index and opened for update,
425          * then we cache the scan controller and conglomerate
426          * number in the activation so that the scan controller
427          * can be re-used by the update/delete if the index
428          * that we are scanning also needs to be updated.
429          */

430         if (forUpdate && isKeyed)
431         {
432             activation.setIndexScanController(scanController);
433             activation.setIndexConglomerateNumber(conglomId);
434         }
435
436         firstScan = false;
437         isOpen = true;
438         numOpens++;
439         nextDone = false;
440         openTime += getElapsedMillis(beginTime);
441     }
442
443     /*
444     ** Open the scan controller
445     **
446     ** @param transaction controller will open one if null
447     */

448     protected void openScanController(TransactionController tc)
449         throws StandardException
450     {
451         DataValueDescriptor[] startPositionRow =
452             startPosition == null ? null : startPosition.getRowArray();
453         DataValueDescriptor[] stopPositionRow =
454             stopPosition == null ? null : stopPosition.getRowArray();
455
456         // Clear the Qualifiers's Orderable cache
457
if (qualifiers != null)
458         {
459             clearOrderableCache(qualifiers);
460         }
461
462         // Get the current transaction controller
463
if (tc == null)
464             tc = activation.getTransactionController();
465
466         int openMode = 0;
467         if (forUpdate)
468         {
469             openMode = TransactionController.OPENMODE_FORUPDATE;
470
471             if (activation.isCursorActivation())
472                 openMode |= TransactionController.OPENMODE_USE_UPDATE_LOCKS;
473         }
474
475         scanController = tc.openCompiledScan(
476                 activation.getResultSetHoldability(),
477                 openMode,
478                 lockMode,
479                 isolationLevel,
480                 accessedCols,
481                 startPositionRow,
482                     // not used when giving null start position
483
startSearchOperator,
484                 qualifiers,
485                 stopPositionRow,
486                     // not used when giving null stop position
487
stopSearchOperator,
488                 scoci,
489                 dcoci);
490
491         /* Remember that we opened the scan */
492         scanControllerOpened = true;
493
494         rowsThisScan = 0;
495
496         /*
497         ** Inform the activation of the estimated number of rows. Only
498         ** do it here, not in reopen, so that we don't do this costly
499         ** check too often.
500         */

501         estimatedRowCount = scanController.getEstimatedRowCount();
502         activation.informOfRowCount(
503                                     this,
504                                     scanController.getEstimatedRowCount()
505                                     );
506     }
507
508     /*
509     ** reopen the scan controller
510     */

511     private void reopenScanController()
512         throws StandardException
513     {
514         DataValueDescriptor[] startPositionRow =
515             startPosition == null ? null : startPosition.getRowArray();
516         DataValueDescriptor[] stopPositionRow =
517             stopPosition == null ? null : stopPosition.getRowArray();
518
519         // Clear the Qualifiers's Orderable cache
520
if (qualifiers != null)
521         {
522             clearOrderableCache(qualifiers);
523         }
524
525         scanController.reopenScan(
526                         startPositionRow,
527                         startSearchOperator,
528                         qualifiers,
529                         stopPositionRow,
530                         stopSearchOperator);
531
532         /* Remember that we opened the scan */
533         scanControllerOpened = true;
534
535         rowsThisScan = 0;
536     }
537
538     /**
539      * Reopen a table scan. Here we take advantage
540      * of the reopenScan() interface on scanController
541      * for optimimal performance on joins where we are
542      * an inner table.
543      *
544      * @exception StandardException thrown on failure to open
545      */

546     public void reopenCore() throws StandardException
547     {
548         beginTime = getCurrentTimeMillis();
549         if (SanityManager.DEBUG)
550             SanityManager.ASSERT(isOpen, "TableScanResultSet not open, cannot reopen");
551
552         if (startKeyGetter != null)
553         {
554             startPosition = (ExecIndexRow) startKeyGetter.invoke(activation);
555             if (sameStartStopPosition)
556             {
557                 stopPosition = startPosition;
558             }
559         }
560         if (stopKeyGetter != null)
561         {
562             stopPosition = (ExecIndexRow) stopKeyGetter.invoke(activation);
563         }
564
565         // Check whether there are any comparisons with unordered nulls
566
// on either the start or stop position. If there are, we can
567
// (and must) skip the scan, because no rows can qualify
568
if (skipScan(startPosition, stopPosition))
569         {
570             scanControllerOpened = false;
571         }
572         else
573         {
574             if (scanController == null)
575                 openScanController((TransactionController)null);
576             else
577                 reopenScanController();
578         
579         }
580
581         numOpens++;
582         nextDone = false;
583         openTime += getElapsedMillis(beginTime);
584     }
585
586     /**
587      * Check and make sure sparse heap row and accessed bit map are created.
588      * beetle 3865, update cursor using index.
589      *
590      * @exception StandardException thrown on failure
591      */

592     private void getSparseRowAndMap() throws StandardException
593     {
594         int numCols = 1, colPos;
595         for (int i = 0; i < indexCols.length; i++)
596         {
597             colPos = (indexCols[i] > 0) ? indexCols[i] : -indexCols[i];
598             if (colPos > numCols)
599                 numCols = colPos;
600         }
601         sparseRow = new ValueRow(numCols);
602         sparseRowMap = new FormatableBitSet(numCols);
603         for (int i = 0; i < indexCols.length; i++)
604         {
605             if (accessedCols.get(i))
606             {
607                 colPos = (indexCols[i] > 0) ? indexCols[i] : -indexCols[i];
608                 sparseRow.setColumn(colPos, candidate.getColumn(i + 1));
609                 sparseRowMap.set(colPos - 1);
610             }
611         }
612     }
613         
614
615     /**
616      * Return the next row (if any) from the scan (if open).
617      *
618      * @exception StandardException thrown on failure to get next row
619      */

620     public ExecRow getNextRowCore() throws StandardException
621     {
622         checkCancellationFlag();
623             
624         if (currentRow == null || scanRepositioned)
625         {
626             currentRow =
627                 getCompactRow(candidate, accessedCols, (FormatableBitSet) null, isKeyed);
628         }
629
630         beginTime = getCurrentTimeMillis();
631
632         ExecRow result = null;
633
634         /* beetle 3865, updateable cursor using index. We first saved updated rows with new value
635          * falling into future direction of index scan in hash table, if it's full, we scanned
636          * forward and saved future row ids in a virtual mem heap.
637          */

638         if (futureForUpdateRows != null)
639         {
640             currentRowPrescanned = false;
641             if (! skipFutureRowHolder)
642             {
643                 if (futureRowResultSet == null)
644                 {
645                     futureRowResultSet = (TemporaryRowHolderResultSet) futureForUpdateRows.getResultSet();
646                     futureRowResultSet.openCore();
647                 }
648
649                 ExecRow ridRow = futureRowResultSet.getNextRowCore();
650
651                 if (ridRow != null)
652                 {
653                     /* to boost performance, we used virtual mem heap, and we can insert after
654                      * we start retrieving results. The assumption is to
655                      * delete current row right after we retrieve it.
656                      */

657                     futureRowResultSet.deleteCurrentRow();
658                     RowLocation rl = (RowLocation) ridRow.getColumn(1);
659                     ConglomerateController baseCC = activation.getHeapConglomerateController();
660                     if (sparseRow == null)
661                         getSparseRowAndMap();
662                     baseCC.fetch(
663                               rl, sparseRow.getRowArray(), sparseRowMap);
664                     RowLocation rl2 = (RowLocation) rl.getClone();
665                     currentRow.setColumn(currentRow.nColumns(), rl2);
666                     candidate.setColumn(candidate.nColumns(), rl2); // have to be consistent!
667

668                     result = currentRow;
669                     currentRowPrescanned = true;
670                 }
671                 else if (sourceDrained)
672                 {
673                     currentRowPrescanned = true;
674                     currentRow = null;
675                 }
676
677                 if (currentRowPrescanned)
678                 {
679                     setCurrentRow(result);
680
681                     nextTime += getElapsedMillis(beginTime);
682                     return result;
683                 }
684             }
685         }
686
687         if ( isOpen && !nextDone)
688         {
689             /* Only need to do 1 next per scan
690              * for 1 row scans.
691              */

692             nextDone = oneRowScan;
693
694             if (scanControllerOpened)
695             {
696                 boolean moreRows;
697
698                 while (moreRows =
699                             scanController.fetchNext(candidate.getRowArray()))
700                 {
701                     rowsSeen++;
702                     rowsThisScan++;
703
704                     /*
705                     ** Skip rows where there are start or stop positioners
706                     ** that do not implement ordered null semantics and
707                     ** there are columns in those positions that contain
708                     ** null.
709                     ** No need to check if start and stop positions are the
710                     ** same, since all predicates in both will be ='s,
711                     ** and hence evaluated in the store.
712                     */

713                     if ((! sameStartStopPosition) && skipRow(candidate))
714                     {
715                         rowsFiltered++;
716                         continue;
717                     }
718
719                     /* beetle 3865, updateable cursor use index. If we have a hash table that
720                      * holds updated records, and we hit it again, skip it, and remove it from
721                      * hash since we can't hit it again, and we have a space in hash, so can
722                      * stop scanning forward.
723                      */

724                     if (past2FutureTbl != null)
725                     {
726                         RowLocation rowLoc = (RowLocation) currentRow.getColumn(currentRow.nColumns());
727                         if (past2FutureTbl.get(rowLoc) != null)
728                         {
729                             past2FutureTbl.remove(rowLoc);
730                             continue;
731                         }
732                     }
733
734                     result = currentRow;
735
736                     break;
737                 }
738
739                 /*
740                 ** If we just finished a full scan of the heap, update
741                 ** the number of rows in the scan controller.
742                 **
743                 ** NOTE: It would be more efficient to only update the
744                 ** scan controller if the optimizer's estimated number of
745                 ** rows were wrong by more than some threshold (like 10%).
746                 ** This would require a little more work than I have the
747                 ** time for now, however, as the row estimate that is given
748                 ** to this result set is the total number of rows for all
749                 ** scans, not the number of rows per scan.
750                 */

751                 if (! moreRows)
752                 {
753                     setRowCountIfPossible(rowsThisScan);
754                     currentRow = null;
755                 }
756             }
757         }
758
759         setCurrentRow(result);
760         currentRowIsValid = true;
761         scanRepositioned = false;
762         qualify = true;
763
764         nextTime += getElapsedMillis(beginTime);
765         return result;
766     }
767
768     /**
769      * If the result set has been opened,
770      * close the open scan.
771      * @exception StandardException on error
772      */

773     public void close() throws StandardException
774     {
775         beginTime = getCurrentTimeMillis();
776         if ( isOpen )
777         {
778             /*
779             ** If scan tracing is turned on, print information about this
780             ** TableScanResultSet when it is closed.
781             */

782             if (SanityManager.DEBUG)
783             {
784                 if (SanityManager.DEBUG_ON("ScanTrace"))
785                 {
786                     //traceClose();
787
}
788             }
789
790             // we don't want to keep around a pointer to the
791
// row ... so it can be thrown away.
792
// REVISIT: does this need to be in a finally
793
// block, to ensure that it is executed?
794
clearCurrentRow();
795 ;
796             if (scanController != null)
797             {
798                 // This is where we get the positioner info for inner tables
799
if (runTimeStatisticsOn)
800                 {
801                     // This is where we get the scan properties for a subquery
802
scanProperties = getScanProperties();
803                     startPositionString = printStartPosition();
804                     stopPositionString = printStopPosition();
805                 }
806                 scanController.close();
807                 scanController = null; // should not access after close
808
activation.clearIndexScanInfo();
809             }
810             scanControllerOpened = false;
811             startPosition = null;
812             stopPosition = null;
813
814             super.close();
815
816             if (indexCols != null)
817             {
818                 ConglomerateController borrowedBaseCC = activation.getHeapConglomerateController();
819                 if (borrowedBaseCC != null)
820                 {
821                     borrowedBaseCC.close();
822                     activation.clearHeapConglomerateController();
823                 }
824             }
825             if (futureRowResultSet != null)
826                 futureRowResultSet.close();
827         }
828         else
829             if (SanityManager.DEBUG)
830                 SanityManager.DEBUG("CloseRepeatInfo","Close of TableScanResultSet repeated");
831
832         closeTime += getElapsedMillis(beginTime);
833     }
834
835     /**
836      * Return the total amount of time spent in this ResultSet
837      *
838      * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
839      * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
840      *
841      * @return long The total amount of time spent (in milliseconds).
842      */

843     public long getTimeSpent(int type)
844     {
845         long totTime = constructorTime + openTime + nextTime + closeTime;
846
847         /* RESOLVE - subtract out store time later, when available */
848         if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY)
849         {
850             return totTime;
851         }
852         else
853         {
854             return totTime;
855         }
856     }
857
858
859     //
860
// CursorResultSet interface
861
//
862

863     /**
864      * This result set has its row location from
865      * the last fetch done. If the cursor is closed,
866      * or the row has been deleted a null is returned.
867      *
868      * @see CursorResultSet
869      *
870      * @return the row location of the current cursor row.
871      * @exception StandardException thrown on failure to get row location
872      */

873     public RowLocation getRowLocation() throws StandardException
874     {
875         RowLocation rl;
876
877         if (! isOpen) return null;
878
879         if ( ! scanControllerOpened)
880             return null;
881
882         /*
883         ** If the conglomerate is keyed, the row location of the base row
884         ** is in the last column of the current row. If it's not keyed,
885         ** we get the row location from the scan of the heap.
886         */

887         if (isKeyed)
888         {
889             if (SanityManager.DEBUG)
890             {
891                 SanityManager.ASSERT(currentRow != null,
892                   "There must be a current row when fetching the row location");
893             }
894
895             rl = (RowLocation) currentRow.getColumn(
896                                                     currentRow.nColumns());
897         }
898         else
899         {
900             if (currentRowIsValid) {
901                 // we reuse the same rowlocation object across several calls.
902
if (rlTemplate == null)
903                     rlTemplate = scanController.newRowLocationTemplate();
904                 rl = rlTemplate;
905                 try {
906                     scanController.fetchLocation(rl);
907                 } catch (StandardException se) {
908                     if (se.getMessageId().
909                         equals(SQLState.HEAP_SCAN_NOT_POSITIONED)) {
910                         //Have a easier to understand error message than what
911
//we get from store
912
throw StandardException.
913                             newException(SQLState.NO_CURRENT_ROW);
914                     }
915                     throw se;
916                 }
917             } else {
918                 rl = null;
919             }
920         }
921
922         return rl;
923     }
924
925     /**
926      * This result set has its row from the last fetch done.
927      * If the cursor is closed, the row has been deleted, or
928      * no longer qualifies (for forward only result sets) a
929      * null is returned.
930      *
931      * @see CursorResultSet
932      *
933      * @return the last row returned;
934      * @exception StandardException thrown on failure.
935      */

936     /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),
937      * once there is such a method. (currentRow is redundant)
938      */

939     public ExecRow getCurrentRow() throws StandardException
940     {
941         ExecRow result = null;
942
943         if (SanityManager.DEBUG)
944             SanityManager.ASSERT(isOpen, "TSRS expected to be open");
945
946         if (currentRowPrescanned)
947             return currentRow;
948
949         /* Nothing to do if we're not currently on a row or
950          * if the current row get deleted out from under us
951          * or if there is no current scan (can happen if the
952          * scan is being skipped) or if the current position
953          * no longer qualifies.
954          */

955         try
956         {
957             if ((currentRow == null) ||
958             (!currentRowIsValid) ||
959             (!scanControllerOpened) ||
960             (qualify && scanController.isCurrentPositionDeleted()) ||
961             (qualify && (!scanController.doesCurrentPositionQualify())))
962             {
963                 return null;
964             }
965         }
966         catch (StandardException se)
967         {
968             if (se.getMessageId().equals(SQLState.AM_SCAN_NOT_POSITIONED))
969             {
970                 //bug 4515 - Have a easier to understand error message than what we get from store
971
se=StandardException.newException(SQLState.NO_CURRENT_ROW);
972                 throw se;
973             }
974         }
975
976         result = (ExecRow) resultRowAllocator.invoke(activation);
977         currentRow =
978             getCompactRow(result, accessedCols, (FormatableBitSet) null, isKeyed);
979
980         try
981         {
982             scanController.fetchWithoutQualify(result.getRowArray());
983         }
984         catch (StandardException se)
985         {
986             if (se.getMessageId().equals(SQLState.AM_RECORD_NOT_FOUND))
987             {
988                 // Somehow the row got deleted between the above
989
// doesCurrentPositionQualify() call and here (one way is if
990
// this scan is read uncommitted isolation level).
991
return null;
992             }
993             else
994             {
995                 throw se;
996             }
997         }
998
999         setCurrentRow(result);
1000        return currentRow;
1001    }
1002
1003    /**
1004     * @see NoPutResultSet#positionScanAtRowLocation
1005     *
1006     * Also sets qualify to false so that later calls to getCurrentRow
1007     * will not attempt to re-qualify the current row.
1008     */

1009    public void positionScanAtRowLocation(RowLocation rl)
1010        throws StandardException
1011    {
1012        // Check if the scanController is a B-tree scan controller. Do not
1013
// attempt to re-position a b-tree controller.
1014
if (!isKeyed) {
1015            currentRowIsValid = scanController.positionAtRowLocation(rl);
1016        }
1017        qualify = false;
1018        scanRepositioned = true;
1019    }
1020
1021    /**
1022     * Print the parameters that constructed this result set to the
1023     * trace stream.
1024     */

1025/*
1026    private final void traceScanParameters()
1027    {
1028        if (SanityManager.DEBUG)
1029        {
1030            HeaderPrintWriter traceStream = SanityManager.GET_DEBUG_STREAM();
1031
1032            traceStream.println("");
1033            traceStream.println("TableScanResultSet number " +
1034                                resultSetNumber +
1035                                " parameters:");
1036
1037            traceStream.println("");
1038            traceStream.println("\tTable name: " + tableName);
1039            if (indexName != null)
1040            {
1041                traceStream.println("\tIndex name: " + indexName);
1042            }
1043            traceStream.println("");
1044            traceStream.println("\tStart position is: ");
1045            tracePrintPosition(traceStream,
1046                                startSearchOperator,
1047                                startKeyGetter);
1048            traceStream.println("");
1049            traceStream.println("\tStop position is: " );
1050            tracePrintPosition(traceStream,
1051                                stopSearchOperator,
1052                                stopKeyGetter);
1053            traceStream.println("");
1054            traceStream.println("\tQualifiers are: ");
1055            tracePrintQualifiers(traceStream, qualifiers, 2);
1056            traceStream.println("");
1057        }
1058    }
1059*/

1060
1061    /**
1062     * Print I/O statistics about a scan when it closes.
1063     */

1064/*
1065    private final void traceClose()
1066    {
1067        if (SanityManager.DEBUG)
1068        {
1069            InfoStreams infoStreams;
1070            HeaderPrintWriter traceStream;
1071
1072            traceStream = SanityManager.GET_DEBUG_STREAM();
1073
1074            traceStream.println("TableScanResultSet number " +
1075                                resultSetNumber +
1076                                " closed.");
1077            if (isKeyed)
1078            {
1079                traceStream.println("\t" +
1080                                    rowCount() +
1081                                    " row(s) qualified from " +
1082                                    "keyed" +
1083                                    " table " +
1084                                    tableName +
1085                                    " using index " +
1086                                    indexName);
1087            }
1088            else
1089            {
1090                traceStream.println("\t" +
1091                                    rowCount() +
1092                                    " row(s) qualified from " +
1093                                    "non-keyed" +
1094                                    " table " +
1095                                    tableName);
1096            }
1097            traceStream.println("");
1098        }
1099    }
1100*/

1101
1102    /**
1103     * Print a start or stop positioner to the trace stream.
1104     */

1105/*
1106    private final void tracePrintPosition(HeaderPrintWriter traceStream,
1107                                          int searchOperator,
1108                                          GeneratedMethod positionGetter)
1109    {
1110        if (SanityManager.DEBUG)
1111        {
1112            if (positionGetter == null)
1113            {
1114                traceStream.println("\t\tNone");
1115                return;
1116            }
1117
1118            ExecIndexRow positioner = null;
1119
1120            try
1121            {
1122                positioner = (ExecIndexRow) positionGetter.invoke(activation);
1123            }
1124            catch (StandardException e)
1125            {
1126                traceStream.println("\t\tUnexpected exception " +
1127                                    e +
1128                                    " getting positioner.");
1129                e.printStackTrace(traceStream.getPrintWriter());
1130                return;
1131            }
1132
1133            if (positioner == null)
1134            {
1135                traceStream.println("\t\tNone");
1136                return;
1137            }
1138
1139            String searchOp = null;
1140
1141            switch (searchOperator)
1142            {
1143              case ScanController.GE:
1144                searchOp = "GE";
1145                break;
1146
1147              case ScanController.GT:
1148                searchOp = "GT";
1149                break;
1150
1151              default:
1152                searchOp = "unknown value (" + searchOperator + ")";
1153                break;
1154            }
1155
1156            traceStream.println("\t\t" +
1157                                searchOp +
1158                                " on first " +
1159                                positioner.nColumns() +
1160                                " column(s).");
1161
1162            traceStream.print(
1163                    "\t\tOrdered null semantics on the following columns: ");
1164            for (int position = 0; position < positioner.nColumns(); position++)
1165            {
1166                if (positioner.areNullsOrdered(position))
1167                {
1168                    traceStream.print(position + " ");
1169                }
1170            }
1171            traceStream.println("");
1172        }
1173    }
1174*/

1175
1176
1177    /**
1178     * Print an array of Qualifiers to the trace stream.
1179     */

1180/*
1181    private final void tracePrintQualifiers(HeaderPrintWriter traceStream,
1182                                            Qualifier[][] qualifiers,
1183                                            int depth)
1184    {
1185        if (SanityManager.DEBUG)
1186        {
1187            char[] indentchars = new char[depth];
1188
1189            /*
1190            ** Form an array of tab characters for indentation.
1191            *
1192            while (depth > 0)
1193            {
1194                indentchars[depth - 1] = '\t';
1195                depth--;
1196            }
1197            String indent = new String(indentchars);
1198
1199            if (qualifiers == null)
1200            {
1201                traceStream.println(indent +
1202                                    MessageService.getTextMessage(
1203                                        SQLState.LANG_NONE)
1204                                    );
1205                return;
1206            }
1207
1208            // RESOLVE (mikem) We don't support 2-d qualifiers yet.
1209            if (SanityManager.DEBUG)
1210            {
1211                SanityManager.ASSERT(qualifiers.length == 1);
1212            }
1213
1214            for (int i = 0; i < qualifiers[0].length; i++)
1215            {
1216                Qualifier qual = qualifiers[0][i];
1217
1218                traceStream.println("");
1219                traceStream.println(indent + "Column Id: " + qual.getColumnId());
1220                
1221                int operator = qual.getOperator();
1222                String opString = null;
1223                switch (operator)
1224                {
1225                  case Orderable.ORDER_OP_EQUALS:
1226                    opString = "=";
1227                    break;
1228
1229                  case Orderable.ORDER_OP_LESSOREQUALS:
1230                    opString = "<=";
1231                    break;
1232
1233                  case Orderable.ORDER_OP_LESSTHAN:
1234                    opString = "<";
1235                    break;
1236
1237                  default:
1238                    opString = "unknown value (" + operator + ")";
1239                    break;
1240                }
1241                traceStream.println(indent + "Operator: " + opString);
1242                traceStream.println(indent + "Ordered nulls: " +
1243                                            qual.getOrderedNulls());
1244                traceStream.println(indent + "Unknown return value: " +
1245                                            qual.getUnknownRV());
1246                traceStream.println(indent + "Negate comparison result: " +
1247                                            qual.negateCompareResult());
1248                traceStream.println("");
1249            }
1250        }
1251    }
1252*/

1253
1254    public String JavaDoc printStartPosition()
1255    {
1256        return printPosition(startSearchOperator, startKeyGetter, startPosition);
1257    }
1258
1259    public String JavaDoc printStopPosition()
1260    {
1261        if (sameStartStopPosition)
1262        {
1263            return printPosition(stopSearchOperator, startKeyGetter, startPosition);
1264        }
1265        else
1266        {
1267            return printPosition(stopSearchOperator, stopKeyGetter, stopPosition);
1268        }
1269    }
1270
1271    /**
1272     * Return a start or stop positioner as a String.
1273     *
1274     * If we already generated the information, then use
1275     * that. Otherwise, invoke the activation to get it.
1276     */

1277    private String JavaDoc printPosition(int searchOperator,
1278                                 GeneratedMethod positionGetter,
1279                                 ExecIndexRow positioner)
1280    {
1281                String JavaDoc idt = "";
1282        String JavaDoc output = "";
1283        if (positionGetter == null)
1284        {
1285            return "\t" +
1286                    MessageService.getTextMessage(SQLState.LANG_NONE) +
1287                    "\n";
1288        }
1289        
1290        if (positioner == null)
1291        {
1292            try
1293            {
1294                positioner = (ExecIndexRow)positionGetter.invoke(activation);
1295            }
1296            catch (StandardException e)
1297            {
1298                // the positionGetter will fail with a NullPointerException
1299
// if the outer table is empty
1300
// (this isn't a problem since we won't call it on the inner
1301
// table if there are no rows on the outer table)
1302
if (e.getSQLState() == SQLState.LANG_UNEXPECTED_USER_EXCEPTION )
1303                    return "\t" + MessageService.getTextMessage(
1304                        SQLState.LANG_POSITION_NOT_AVAIL);
1305                return "\t" + MessageService.getTextMessage(
1306                        SQLState.LANG_UNEXPECTED_EXC_GETTING_POSITIONER,
1307                        e.toString());
1308            }
1309        }
1310        if (positioner == null)
1311        {
1312            return "\t" +
1313                    MessageService.getTextMessage(SQLState.LANG_NONE) +
1314                    "\n";
1315        }
1316        String JavaDoc searchOp = null;
1317
1318        switch (searchOperator)
1319        {
1320            case ScanController.GE:
1321                searchOp = ">=";
1322                break;
1323
1324            case ScanController.GT:
1325                searchOp = ">";
1326                break;
1327
1328            default:
1329                if (SanityManager.DEBUG)
1330                {
1331                    SanityManager.THROWASSERT("Unknown search operator " +
1332                                                searchOperator);
1333                }
1334
1335                // NOTE: This does not have to be internationalized because
1336
// this code should never be reached.
1337
searchOp = "unknown value (" + searchOperator + ")";
1338                break;
1339        }
1340
1341        output = output + "\t" +
1342                        MessageService.getTextMessage(
1343                            SQLState.LANG_POSITIONER,
1344                            searchOp,
1345                            String.valueOf(positioner.nColumns())) +
1346                        "\n";
1347
1348        output = output + "\t" +
1349                    MessageService.getTextMessage(
1350                        SQLState.LANG_ORDERED_NULL_SEMANTICS) +
1351                    "\n";
1352        for (int position = 0; position < positioner.nColumns(); position++)
1353        {
1354            if (positioner.areNullsOrdered(position))
1355            {
1356                output = output + position + " ";
1357            }
1358        }
1359        
1360        return output + "\n";
1361    }
1362
1363    public Properties JavaDoc getScanProperties()
1364    {
1365        if (scanProperties == null)
1366        {
1367            scanProperties = new Properties JavaDoc();
1368        }
1369        try
1370        {
1371            if (scanController != null)
1372            {
1373                scanController.getScanInfo().getAllScanInfo(scanProperties);
1374                /* Did we get a coarser lock due to
1375                 * a covering lock, lock escalation
1376                 * or configuration?
1377                 */

1378                coarserLock = scanController.isTableLocked() &&
1379                    (lockMode == TransactionController.MODE_RECORD);
1380            }
1381        }
1382        catch(StandardException se)
1383        {
1384            // ignore
1385
}
1386
1387        return scanProperties;
1388    }
1389
1390    /**
1391     * @see NoPutResultSet#getScanIsolationLevel
1392     */

1393    public int getScanIsolationLevel()
1394    {
1395        return isolationLevel;
1396    }
1397
1398    /**
1399     * @see NoPutResultSet#requiresRelocking
1400     */

1401    public boolean requiresRelocking()
1402    {
1403        return(
1404            isolationLevel ==
1405                TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK);
1406    }
1407
1408    /**
1409     * Update the number of rows in the scan controller.
1410     *
1411     * NOTE: It would be more efficient to only update the
1412     * scan controller if the optimizer's estimated number of
1413     * rows were wrong by more than some threshold (like 10%).
1414     * This would require a little more work than I have the
1415     * time for now, however, as the row estimate that is given
1416     * to this result set is the total number of rows for all
1417     * scans, not the number of rows per scan.
1418     *
1419     *
1420     * @param rowsThisScan The number of rows to update the scanController to
1421     *
1422     * @exception StandardException Thrown on error
1423     */

1424    protected final void setRowCountIfPossible(long rowsThisScan)
1425                    throws StandardException
1426    {
1427        /*
1428        ** Is it a heap scan with no qualifiers (full table scan?)
1429        ** and is it not for update (we don't want to count rows we're
1430        ** about to delete.
1431        */

1432        if ( ( ! scanController.isKeyed() ) &&
1433            (qualifiers == null || qualifiers.length == 0) &&
1434            ( ! forUpdate ) )
1435        {
1436
1437            // Only update rows if different by more than 10%
1438
long diff = rowsThisScan - estimatedRowCount;
1439
1440            long tenPerCent = estimatedRowCount / 10;
1441
1442            if (diff < 0)
1443                diff = -diff;
1444
1445            if (diff > tenPerCent)
1446                scanController.setEstimatedRowCount(rowsThisScan);
1447        }
1448    }
1449
1450    /**
1451     * Can we get instantaneous locks when getting share row
1452     * locks at READ COMMITTED.
1453     */

1454    protected boolean canGetInstantaneousLocks()
1455    {
1456        return false;
1457    }
1458
1459
1460    /**
1461     * Is this ResultSet or it's source result set for update
1462     *
1463     * @return Whether or not the result set is for update.
1464     */

1465    public boolean isForUpdate()
1466    {
1467        return forUpdate;
1468    }
1469
1470    /**
1471     * Shallow clone this result set. Used in trigger reference.
1472     * beetle 4373.
1473     */

1474    public Object JavaDoc clone()
1475    {
1476        Object JavaDoc clo = null;
1477        try {
1478            clo = super.clone();
1479        }
1480        catch (CloneNotSupportedException JavaDoc e) {}
1481        return clo;
1482    }
1483}
1484
Popular Tags