KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.execute.IndexRowToBaseRowResultSet
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.services.monitor.Monitor;
25
26 import org.apache.derby.iapi.services.sanity.SanityManager;
27
28 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
29 import org.apache.derby.iapi.services.stream.InfoStreams;
30
31 import org.apache.derby.iapi.sql.execute.CursorResultSet;
32 import org.apache.derby.iapi.sql.execute.ExecRow;
33 import org.apache.derby.iapi.sql.execute.NoPutResultSet;
34
35 import org.apache.derby.iapi.types.DataValueDescriptor;
36
37 import org.apache.derby.iapi.sql.Activation;
38 import org.apache.derby.iapi.sql.ResultSet;
39
40 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
41 import org.apache.derby.iapi.sql.conn.StatementContext;
42
43 import org.apache.derby.iapi.store.access.ConglomerateController;
44 import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
45 import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
46 import org.apache.derby.iapi.store.access.TransactionController;
47
48 import org.apache.derby.iapi.services.loader.GeneratedMethod;
49
50 import org.apache.derby.iapi.reference.SQLState;
51 import org.apache.derby.iapi.error.StandardException;
52
53 import org.apache.derby.iapi.types.RowLocation;
54
55 import org.apache.derby.iapi.services.io.FormatableBitSet;
56
57 import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
58
59 /**
60  * Takes a result set with a RowLocation as the last column, and uses the
61  * RowLocation to get and return a row from the given base conglomerate.
62  * Normally, the input result set will be a TableScanResultSet scanning an
63  * index conglomerate.
64  *
65  * @author jeff
66  */

67 class IndexRowToBaseRowResultSet extends NoPutResultSetImpl
68     implements CursorResultSet {
69
70     // set in constructor and not altered during
71
// life of object.
72
private long conglomId;
73     public NoPutResultSet source;
74     private GeneratedMethod resultRowAllocator;
75     private GeneratedMethod restriction;
76     private long baseConglomId;
77     public FormatableBitSet accessedHeapCols;
78     private FormatableBitSet accessedIndexCols;
79     //caching accessed columns (heap+index) beetle 3865
80
private FormatableBitSet accessedAllCols;
81     public String JavaDoc indexName;
82     private int[] indexCols;
83     private DynamicCompiledOpenConglomInfo dcoci;
84     private StaticCompiledOpenConglomInfo scoci;
85
86     // set in open() and not changed after that
87
private ConglomerateController baseCC;
88     private boolean closeBaseCCHere;
89     private ExecRow resultRow;
90     private ExecRow compactRow;
91     private boolean forUpdate;
92     private DataValueDescriptor[] rowArray;
93
94     // changed a whole bunch
95
RowLocation baseRowLocation;
96
97     /* Remember whether or not we have copied any
98      * columns from the source row to our row yet.
99      */

100     boolean copiedFromSource;
101
102     /* Run time statistics variables */
103     public long restrictionTime;
104
105     protected boolean currentRowPrescanned;
106     private boolean sourceIsForUpdateIndexScan;
107
108     //
109
// class interface
110
//
111
IndexRowToBaseRowResultSet(
112                     long conglomId,
113                     int scociItem,
114                     Activation a,
115                     NoPutResultSet source,
116                     GeneratedMethod resultRowAllocator,
117                     int resultSetNumber,
118                     String JavaDoc indexName,
119                     int heapColRefItem,
120                     int indexColRefItem,
121                     int indexColMapItem,
122                     GeneratedMethod restriction,
123                     boolean forUpdate,
124                     double optimizerEstimatedRowCount,
125                     double optimizerEstimatedCost)
126         throws StandardException
127     {
128         super(a, resultSetNumber, optimizerEstimatedRowCount, optimizerEstimatedCost);
129         scoci = (StaticCompiledOpenConglomInfo)(activation.getPreparedStatement().
130                         getSavedObject(scociItem));
131         TransactionController tc = activation.getTransactionController();
132         dcoci = tc.getDynamicCompiledConglomInfo(conglomId);
133         this.source = source;
134         this.resultRowAllocator = resultRowAllocator;
135         this.indexName = indexName;
136         this.forUpdate = forUpdate;
137         this.restriction = restriction;
138
139         /* RESOLVE - once we push Qualifiers into the store we
140          * need to clear their Orderable cache on each open/reopen.
141          */

142
143         // retrieve the valid column list from
144
// the saved objects, if it exists
145
this.accessedHeapCols = null;
146         if (heapColRefItem != -1)
147         {
148             this.accessedHeapCols = (FormatableBitSet)(a.getPreparedStatement().
149                         getSavedObject(heapColRefItem));
150         }
151         if (indexColRefItem != -1)
152         {
153             this.accessedIndexCols = (FormatableBitSet)(a.getPreparedStatement().
154                         getSavedObject(indexColRefItem));
155         }
156         if (accessedIndexCols == null)
157             accessedAllCols = accessedHeapCols;
158         else
159         {
160             accessedAllCols = new FormatableBitSet(accessedHeapCols);
161             accessedAllCols.or(accessedIndexCols);
162         }
163             
164         // retrieve the array of columns coming from the index
165
indexCols = ((ReferencedColumnsDescriptorImpl) (a.getPreparedStatement().
166                         getSavedObject(indexColMapItem))).getReferencedColumnPositions();
167
168         /* Get the result row template */
169         resultRow = (ExecRow) resultRowAllocator.invoke(activation);
170
171         compactRow =
172             getCompactRow(resultRow,
173                             accessedHeapCols,
174                             accessedIndexCols,
175                             false);
176
177         /* If there's no partial row bit map, then we want the entire
178          * row, otherwise we need to diddle with the row array so that
179          * we only get the columns coming from the heap on the fetch.
180          */

181         if (accessedHeapCols == null)
182         {
183             rowArray = resultRow.getRowArray();
184         }
185         else
186         {
187             // Figure out how many columns are coming from the heap
188
int arraySize = accessedHeapCols.getNumBitsSet();
189             int accessedHeapColsSize = accessedHeapCols.size();
190
191             rowArray = new DataValueDescriptor[accessedHeapColsSize];
192
193             // Now, fill in rowArray with the desired columns
194
int partialIndex = 0;
195             int numFromIndex = 0;
196             for (int index = 0; index < accessedHeapColsSize; index++)
197             {
198                 if (accessedIndexCols != null && accessedIndexCols.get(index))
199                 {
200                     numFromIndex++;
201                     continue;
202                 }
203                 if (accessedHeapCols.get(index))
204                 {
205                     rowArray[index] =
206                         resultRow.getRowArray()[index];
207                     partialIndex++;
208                 }
209             }
210         }
211
212         constructorTime += getElapsedMillis(beginTime);
213
214     }
215
216     //
217
// ResultSet interface (leftover from NoPutResultSet)
218
//
219

220     /**
221      * open this ResultSet.
222      *
223      * @exception StandardException thrown if cursor finished.
224      */

225     public void openCore() throws StandardException
226     {
227         boolean lockingRequired = false;
228         TransactionController tc;
229
230         // REVISIT: through the direct DB API, this needs to be an
231
// error, not an ASSERT; users can open twice. Only through JDBC
232
// is access to open controlled and ensured valid.
233
if (SanityManager.DEBUG)
234         {
235             SanityManager.ASSERT( ! isOpen,
236                                 "IndexRowToBaseRowResultSet already open");
237         }
238
239         beginTime = getCurrentTimeMillis();
240
241         source.openCore();
242         if ((source instanceof TableScanResultSet) &&
243             ((TableScanResultSet) source).indexCols != null)
244             sourceIsForUpdateIndexScan = true;
245
246         /* Get a ConglomerateController for the base conglomerate
247          * NOTE: We only need to acquire locks on the data pages when
248          * going through the index when we are at READ COMMITTED and
249          * the source is a BulkTableScan or HashScan. (The underlying
250          * row will not be guaranteed to be locked.)
251          */

252         if (source.requiresRelocking())
253         {
254             lockingRequired = true;
255         }
256
257         tc = activation.getTransactionController();
258
259         int openMode;
260         int isolationLevel;
261         
262         if (forUpdate)
263         {
264             openMode = TransactionController.OPENMODE_FORUPDATE;
265         }
266         else
267         {
268             openMode = 0;
269         }
270         isolationLevel = source.getScanIsolationLevel();
271
272         if (!lockingRequired)
273         {
274             // flag indicates that lock has already been acquired by access to
275
// the secondary index, and need not be gotten again in the base
276
// table.
277
openMode |= TransactionController.OPENMODE_SECONDARY_LOCKED;
278         }
279         
280         /* Try to get the ConglomerateController from the activation
281          * first, for the case that we are part of an update or delete.
282          * If so, then the RowChangerImpl did the correct locking.
283          * If not there, then we go off and open it ourself.
284          */

285         if (forUpdate)
286         {
287             baseCC = activation.getHeapConglomerateController();
288         }
289
290         if (baseCC == null)
291         {
292             baseCC =
293                 tc.openCompiledConglomerate(
294                     activation.getResultSetHoldability(),
295                     openMode,
296                     // consistent with FromBaseTable's updateTargetLockMode
297
TransactionController.MODE_RECORD,
298                     isolationLevel,
299                     scoci,
300                     dcoci);
301             closeBaseCCHere = true;
302         }
303
304         isOpen = true;
305         numOpens++;
306         openTime += getElapsedMillis(beginTime);
307     }
308
309     /**
310      * reopen this ResultSet.
311      *
312      * @exception StandardException thrown if cursor finished.
313      */

314     public void reopenCore() throws StandardException {
315         TransactionController tc;
316
317         if (SanityManager.DEBUG)
318         {
319             SanityManager.ASSERT(isOpen,
320                     "IndexRowToBaseRowResultSet already open");
321         }
322
323         beginTime = getCurrentTimeMillis();
324
325         source.reopenCore();
326
327         numOpens++;
328         openTime += getElapsedMillis(beginTime);
329     }
330
331     /**
332      * Return the requested values computed
333      * from the next row (if any) for which
334      * the restriction evaluates to true.
335      * <p>
336      * restriction and projection parameters
337      * are evaluated for each row.
338      *
339      * @exception StandardException thrown on failure.
340      * @exception StandardException ResultSetNotOpen thrown if not yet open.
341      *
342      * @return the next row in the result
343      */

344     public ExecRow getNextRowCore() throws StandardException {
345
346         ExecRow sourceRow = null;
347         ExecRow retval = null;
348         boolean restrict = false;
349         DataValueDescriptor restrictBoolean;
350         long beginRT = 0;
351
352         beginTime = getCurrentTimeMillis();
353         if ( ! isOpen ) {
354             throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, "next");
355         }
356
357         /* beetle 3865, updateable cursor using index. When in-memory hash table was full, we
358          * read forward and saved future row id's in a virtual-memory-like temp table. So if
359          * we have rid's saved, and we are here, it must be non-covering index. Intercept it
360          * here, so that we don't have to go to underlying index scan. We get both heap cols
361          * and index cols together here for better performance.
362          */

363         if (sourceIsForUpdateIndexScan && ((TableScanResultSet) source).futureForUpdateRows != null)
364         {
365             currentRowPrescanned = false;
366             TableScanResultSet src = (TableScanResultSet) source;
367
368             if (src.futureRowResultSet == null)
369             {
370                 src.futureRowResultSet = (TemporaryRowHolderResultSet) src.futureForUpdateRows.getResultSet();
371                 src.futureRowResultSet.openCore();
372             }
373
374             ExecRow ridRow = src.futureRowResultSet.getNextRowCore();
375
376             currentRow = null;
377
378             if (ridRow != null)
379             {
380                 /* To maximize performance, we only use virtual memory style heap, no
381                  * position index is ever created. And we save and retrieve rows from the
382                  * in-memory part of the heap as much as possible. We can also insert after
383                  * we start retrieving, the assumption is that we delete the current row right
384                  * after we retrieve it.
385                  */

386                 src.futureRowResultSet.deleteCurrentRow();
387                 baseRowLocation = (RowLocation) ridRow.getColumn(1);
388                 baseCC.fetch(
389                           baseRowLocation, compactRow.getRowArray(), accessedAllCols);
390
391                 currentRow = compactRow;
392                 currentRowPrescanned = true;
393             }
394             else if (src.sourceDrained)
395                 currentRowPrescanned = true;
396
397             if (currentRowPrescanned)
398             {
399                 setCurrentRow(currentRow);
400
401                 nextTime += getElapsedMillis(beginTime);
402                 return currentRow;
403             }
404         }
405
406         /* Loop until we get a row from the base page that qualifies or
407          * there's no more rows from the index that qualify. (If the RID
408          * returned by the index does not qualify, then we have to go back
409          * to the index to see if there is another RID to consider.)
410          */

411         do
412         {
413             sourceRow = source.getNextRowCore();
414
415             if (sourceRow != null) {
416
417                 if (SanityManager.DEBUG) {
418                     SanityManager.ASSERT(
419                         sourceRow.getColumn(sourceRow.nColumns())
420                                                         instanceof RowLocation,
421                         "Last column of source row is not a RowLocation"
422                             );
423                 }
424
425                 baseRowLocation = (RowLocation)
426                         sourceRow.getColumn(sourceRow.nColumns());
427
428                 // Fetch the columns coming from the heap
429
boolean row_exists =
430                     baseCC.fetch(
431                         baseRowLocation, rowArray, accessedHeapCols);
432
433                 if (row_exists)
434                 {
435                     /* We only need to copy columns from the index row
436                      * to our result row once as we will be reusing the
437                      * wrappers in that case.
438                      * NOTE: When the underlying ResultSet got an
439                      * instantaneous lock (BulkTableScan or HashScan)
440                      * then we will be getting all of the columns anew
441                      * from the index (indexCols == null).
442                      */

443                     if (! copiedFromSource)
444                     {
445                         copiedFromSource = true;
446
447                         // Copy the columns coming from the index into resultRow
448
for (int index = 0; index < indexCols.length; index++)
449                         {
450                             if (indexCols[index] != -1)
451                             {
452                                 compactRow.setColumn(
453                                             index + 1,
454                                             sourceRow.getColumn(indexCols[index] + 1));
455                             }
456                         }
457                     }
458
459                     setCurrentRow(compactRow);
460
461                     restrictBoolean = (DataValueDescriptor)
462                         ((restriction == null) ?
463                              null : restriction.invoke(activation));
464
465                     restrictionTime += getElapsedMillis(beginRT);
466
467                     // if the result is null, we make it false --
468
// so the row won't be returned.
469
restrict = (restrictBoolean == null) ||
470                                 ((! restrictBoolean.isNull()) &&
471                                     restrictBoolean.getBoolean());
472                 }
473
474                 if (! restrict || ! row_exists)
475                 {
476                     rowsFiltered++;
477                     clearCurrentRow();
478                     baseRowLocation = null;
479
480                 }
481                 else
482                 {
483                     currentRow = compactRow;
484                 }
485
486                 /* Update the run time statistics */
487                 rowsSeen++;
488
489                 retval = currentRow;
490             } else {
491                 clearCurrentRow();
492                 baseRowLocation = null;
493
494                 retval = null;
495             }
496         }
497         while ( (sourceRow != null) && (! restrict ) );
498
499         nextTime += getElapsedMillis(beginTime);
500         return retval;
501     }
502
503     /**
504      * If the result set has been opened,
505      * close the open scan.
506      *
507      * @exception StandardException thrown on error
508      */

509     public void close() throws StandardException
510     {
511         beginTime = getCurrentTimeMillis();
512         if ( isOpen ) {
513
514             // we don't want to keep around a pointer to the
515
// row ... so it can be thrown away.
516
// REVISIT: does this need to be in a finally
517
// block, to ensure that it is executed?
518
clearCurrentRow();
519
520             if (closeBaseCCHere)
521             {
522                 // This check should only be needed in the error case where
523
// we may call this close() routine as part of transaction
524
// backout cleanup if any of the following routines fail.
525
// If one of the subsequent statements gets an error, we
526
// will try to close this result set as part of transaction
527
// cleanup, and without this check we get a null pointer
528
// exception because we have null'd out baseCC.
529

530                 if (baseCC != null)
531                     baseCC.close();
532             }
533
534             /* Make sure to null out baseCC since
535              * we check for null baseCC after looking
536              * in the StatementContext.
537              */

538             baseCC = null;
539             source.close();
540
541             super.close();
542         }
543         else if (SanityManager.DEBUG) {
544             SanityManager.DEBUG("CloseRepeatInfo","Close of IndexRowToBaseRowResultSet repeated");
545         }
546
547         closeTime += getElapsedMillis(beginTime);
548     }
549
550     /**
551      * Return the total amount of time spent in this ResultSet
552      *
553      * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
554      * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
555      *
556      * @return long The total amount of time spent (in milliseconds).
557      */

558     public long getTimeSpent(int type)
559     {
560         long totTime = constructorTime + openTime + nextTime + closeTime;
561
562         if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY)
563         {
564             return totTime - source.getTimeSpent(ENTIRE_RESULTSET_TREE);
565         }
566         else
567         {
568             return totTime;
569         }
570     }
571
572     //
573
// CursorResultSet interface
574
//
575
/**
576      * Return the RowLocation of the base row.
577      *
578      * @see CursorResultSet
579      *
580      * @return the row location of the current cursor row.
581      * @exception StandardException thrown on failure.
582      */

583     public RowLocation getRowLocation() throws StandardException {
584         return baseRowLocation;
585     }
586
587     /**
588      * @see NoPutResultSet#positionScanAtRowLocation
589      *
590      * Also remembers row location so that subsequent invocations of
591      * getCurrentRow will not read the index row to look up the row
592      * location base row, but reuse the saved row location.
593      */

594     public void positionScanAtRowLocation(RowLocation rl)
595         throws StandardException
596     {
597         baseRowLocation = rl;
598         source.positionScanAtRowLocation(rl);
599     }
600
601     /** * Gets last row returned.
602      *
603      * @see CursorResultSet
604      *
605      * @return the last row returned.
606      * @exception StandardException thrown on failure.
607      */

608     /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),
609      * once there is such a method. (currentRow is redundant)
610      */

611     public ExecRow getCurrentRow() throws StandardException {
612         ExecRow sourceRow = null;
613
614         if (SanityManager.DEBUG) {
615             SanityManager.ASSERT(isOpen,
616                     "IndexRowToBaseRowResultSet is expected to be open");
617         }
618
619         if (currentRowPrescanned)
620             return currentRow;
621
622         /* Nothing to do if we're not currently on a row */
623         if (currentRow == null)
624         {
625             return null;
626         }
627
628         // We do not need to read the row from the index first, since we already
629
// have the rowLocation of the current row and can read it directly from
630
// the heap.
631
sourceRow = activation.getExecutionFactory().
632                 getValueRow(indexCols.length);
633         sourceRow.setRowArray(rowArray);
634         // Fetch the columns coming from the heap
635
boolean row_exists =
636             baseCC.fetch(
637                 baseRowLocation, rowArray, (FormatableBitSet) null);
638         if (row_exists) {
639             setCurrentRow(sourceRow);
640         } else {
641             clearCurrentRow();
642         }
643         return currentRow;
644     }
645
646     /**
647      * Is this ResultSet or it's source result set for update.
648      * beetle 3865: updateable cursor using index scan. We didn't need this function
649      * before because we couldn't use index for update cursor.
650      *
651      * @return Whether or not the result set is for update.
652      */

653     public boolean isForUpdate()
654     {
655         return source.isForUpdate();
656     }
657
658 }
659
Popular Tags