KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > store > access > conglomerate > GenericScanController


1 /*
2
3    Derby - Class org.apache.derby.impl.store.access.conglomerate.GenericScanController
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.store.access.conglomerate;
23
24 import org.apache.derby.iapi.reference.SQLState;
25
26 import org.apache.derby.iapi.services.sanity.SanityManager;
27
28 import org.apache.derby.iapi.error.StandardException;
29
30 import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;
31 import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
32 import org.apache.derby.iapi.store.access.conglomerate.ScanManager;
33 import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
34
35 import org.apache.derby.iapi.store.access.ConglomerateController;
36 import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
37 import org.apache.derby.iapi.store.access.Qualifier;
38 import org.apache.derby.iapi.store.access.RowUtil;
39 import org.apache.derby.iapi.store.access.ScanController;
40 import org.apache.derby.iapi.store.access.ScanInfo;
41 import org.apache.derby.iapi.store.access.SpaceInfo;
42
43 import org.apache.derby.iapi.store.raw.ContainerHandle;
44 import org.apache.derby.iapi.store.raw.FetchDescriptor;
45 import org.apache.derby.iapi.store.raw.Page;
46 import org.apache.derby.iapi.store.raw.RecordHandle;
47 import org.apache.derby.iapi.store.raw.Transaction;
48
49 import org.apache.derby.iapi.store.access.Qualifier;
50
51 import org.apache.derby.iapi.types.DataValueDescriptor;
52
53 import org.apache.derby.iapi.types.Orderable;
54 import org.apache.derby.iapi.types.RowLocation;
55
56
57 import org.apache.derby.iapi.store.access.BackingStoreHashtable;
58 import org.apache.derby.iapi.services.io.FormatableBitSet;
59
60 import java.util.Properties JavaDoc;
61
62
63 /**
64 Generic class implementing shared ScanController methods.
65
66 Logically a scancontroller is used to scan a set of rows that meet some
67 specified qualification. Rows that meet the qualification may be operated
68 upon by the scan to fetch, delete, or replace. The ScanController also
69 supports the notion or "repositioning" the scan, which simply resets the
70 beginning of the scan to a new place, and allows the user to continue from
71 there.
72
73 This class attempts to abstract out some of the parts of the scan such that
74 maybe multiple access methods can share code, even if they perform parts of
75 the scan wildly differently. Here is how the scan has been broken apart:
76
77 scan_position - this variable holds the current scan position, it may be
78                 extended
79                 to provide more information if necessary.
80
81 scan_state - a scan has 3 possible states:
82                 SCAN_INIT, SCAN_INPROGRESS, SCAN_DONE
83
84 positionAtInitScan()
85               - This routine is called to move the scan to the SCAN_INIT state.
86                 It is used both for initialization of the ScanController and
87                 by reopenScan().
88
89 positionAtStartForForwardScan()
90               - This routine is called to move the scan from SCAN_INIT to
91                 SCAN_INPROGRESS. Upon return from this routine it is expected
92                 that scan_position is set such that calling the generic
93                 scan loop will reach the first row of the scan. Note that this
94                 usually means setting the scan_postion to one before the 1st
95                 row to be returned.
96
97 fetchRows() - This routine is the meat of the scan, it moves the scan to the
98                 next row, applies necessary qualifiers, and handles group or
99                 non-group operations. It moves through rows on a page in
100                 order and then moves to the "next" page.
101
102 positionAtNextPage()
103               - This routine handles moving the scan from the current
104                 scan_position to the next page.
105
106 positionAtDoneScan()
107               - Handle all cleanup associated with moving the scan state from
108                 SCAN_INPROGRESS to SCAN_DONE. This may include releasing locks,
109                 and setting the state of the scan. This does not close the
110                 scan, it allows for a reopenScan() to be called.
111 **/

112
113 public abstract class GenericScanController
114     extends GenericController implements ScanManager
115 {
116
117     /**************************************************************************
118      * Constants of the class
119      **************************************************************************
120      */

121
122     /*
123      * There are 5 states a scan can be in.
124      * SCAN_INIT - A scan has started but no positioning has been done.
125      * The scan will be positioned when the first next() call
126      * has been made. None of the positioning state variables
127      * are valid in this state.
128      * SCAN_INPROGRESS -
129      * A scan is in this state after the first next() call.
130      * On exit from any GenericScanController method, while in
131      * this state,
132      * the scan "points" at a row which qualifies for the
133      * scan. While not maintaining latches on a page the
134      * current position of the scan is either kept by record
135      * handle or key. To tell which use the following:
136      * if (record key == null)
137      * record handle has current position
138      * else
139      * record key has current position
140      *
141      * SCAN_DONE - Once the end of the table or the stop condition is met
142      * then the scan is placed in this state. Only valid
143      * ScanController method at this point is close().
144      *
145      * SCAN_HOLD_INIT -
146      * The scan has been opened and held open across a commit,
147      * at the last commit the state was SCAN_INIT.
148      * The scan has never progressed from the SCAN_INIT state
149      * during a transaction. When a next is done the state
150      * will either progress to SCAN_INPROGRESS or SCAN_DONE.
151      *
152      * SCAN_HOLD_INPROGRESS -
153      * The scan has been opened and held open across a commit,
154      * at the last commit the state was in SCAN_INPROGRESS.
155      * The transaction which opened the scan has committed,
156      * but the scan was opened with the "hold" option true.
157      * At commit the locks were released and the "current"
158      * position is remembered. In this state only two calls
159      * are valid, either next() or close(). When next() is
160      * called the scan is reopened, the underlying container
161      * is opened thus associating all new locks with the current
162      * transaction, and the scan continues at the "next" row.
163      */

164     public static final int SCAN_INIT = 1;
165     public static final int SCAN_INPROGRESS = 2;
166     public static final int SCAN_DONE = 3;
167     public static final int SCAN_HOLD_INIT = 4;
168     public static final int SCAN_HOLD_INPROGRESS = 5;
169
170     /**************************************************************************
171      * Fields of the class
172      **************************************************************************
173      */

174
175     /**
176      * The following group of fields are all basic input parameters which are
177      * provided by the calling code when doing a scan.
178      * These are just saved values from what was initially input.
179      **/

180     private FormatableBitSet init_scanColumnList;
181     private DataValueDescriptor[] init_startKeyValue;
182     private int init_startSearchOperator;
183     private Qualifier[][] init_qualifier;
184     private DataValueDescriptor[] init_stopKeyValue;
185     private int init_stopSearchOperator;
186
187     private FetchDescriptor init_fetchDesc;
188
189     /**
190      * Delay positioning the table at the start position until the first
191      * next() call.
192      */

193     private int scan_state;
194
195          
196     /**
197      * If this flag is set to true, a RowLocation returned from this controller
198      * may have been reused for another row.
199      */

200     protected boolean rowLocationsInvalidated = false;
201     
202     /**
203      * This is the sequence number for when a record id can be
204      * reused. If it has been changed in the container, a RowLocation
205      * may be reused for another row.
206      */

207     private long reusableRecordIdSequenceNumber = 0;
208     
209     /**
210      * The position for the current scan. The can be maintained in any
211      * of the following ways:
212      * record handle - scan_position.current_rh:
213      * The scan maintains it's position using the record handle while
214      * it does not have a latch on the page, which is the case anytime
215      * control leaves access. The access method must take appropriate
216      * steps to make sure the record handle will still be valid when
217      * the scan needs to reposition using the record handle.
218      * slot number - scan_position.current_slot:
219      * While the scan has a latch on the page the scan is positioned
220      * using the slot number as the order of the rows cannot change
221      * while the latch is held (unless the holder of the latch causes
222      * them to move).
223      * page number - (RESOLVE - TODO)
224      * Sometimes it would be interesting to position a scan "between"
225      * pages, such that the next time the scan starts is starts at
226      * the next page. This would allow us to efficiently do group
227      * scans returning page at atime results.
228      * NOT IMPLEMENTED CURRENTLY.
229      **/

230     protected RowPosition scan_position;
231
232     /**
233      * Performance counters ...
234      */

235     protected int stat_numpages_visited = 0;
236     protected int stat_numrows_visited = 0;
237     protected int stat_numrows_qualified = 0;
238
239     /**************************************************************************
240      * Constructors for This class:
241      **************************************************************************
242      */

243
244     /**************************************************************************
245      * Private methods of This class:
246      **************************************************************************
247      */

248
249     private final void repositionScanForUpateOper()
250         throws StandardException
251     {
252         if (scan_state != SCAN_INPROGRESS)
253             throw StandardException.newException(
254                     SQLState.AM_SCAN_NOT_POSITIONED);
255
256
257         if (!open_conglom.latchPage(scan_position))
258         {
259             throw StandardException.newException(
260                     SQLState.AM_RECORD_NOT_FOUND,
261                     open_conglom.getContainer().getId(),
262                     new Long JavaDoc(scan_position.current_rh.getId()));
263         }
264
265         if (open_conglom.isUseUpdateLocks())
266         {
267             // we only have an U lock at this point which was acquired when the
268
// scan positioned on the row, need to request an
269
// X lock before we can actually perform the delete
270

271             open_conglom.lockPositionForWrite(
272                 scan_position, false /* not insert */, true);
273         }
274     }
275
276
277     /**************************************************************************
278      * Protected methods implementing mechanics of scanning rows:
279      *
280      * positionAtInitScan() - move scan state to SCAN_INIT
281      * positionAtStartForForwardScan() - SCAN_INIT -> SCAN_INPROGRESS
282      * positionAtResumeScan() - reposition after losing scan latch
283      * fetchRows() - move scan while in SCAN_INPROGRESS
284      * positionAtNextPage() - move page while in SCAN_INPROGRESS
285      * positionAtDoneScan() - SCAN_INPROGRESS -> SCAN_DONE
286      *
287      **************************************************************************
288      */

289
290     /**
291      * Move scan to the the SCAN_INIT state.
292      * <p>
293      * This routine is called to move the scan to the SCAN_INIT state.
294      * It is used both for initialization of the ScanController and
295      * by reopenScan().
296      **/

297     protected void positionAtInitScan(
298     DataValueDescriptor[] startKeyValue,
299     int startSearchOperator,
300     Qualifier qualifier[][],
301     DataValueDescriptor[] stopKeyValue,
302     int stopSearchOperator,
303     RowPosition pos)
304         throws StandardException
305     {
306         // startKeyValue init.
307
this.init_startKeyValue = startKeyValue;
308         if (RowUtil.isRowEmpty(this.init_startKeyValue))
309             this.init_startKeyValue = null;
310
311         // startSearchOperator init.
312
this.init_startSearchOperator = startSearchOperator;
313
314         // qualifier init.
315
if ((qualifier != null) && (qualifier .length == 0))
316             qualifier = null;
317         this.init_qualifier = qualifier;
318
319         // TODO (mikem) - this could be more efficient, by writing
320
// code to figure out length of row, but scratch row is cached
321
// so allocating it here is probably not that bad.
322
init_fetchDesc =
323             new FetchDescriptor(
324               (open_conglom.getRuntimeMem().get_scratch_row()).length,
325               init_scanColumnList,
326               init_qualifier);
327
328         // stopKeyValue init.
329
this.init_stopKeyValue = stopKeyValue;
330         if (RowUtil.isRowEmpty(this.init_stopKeyValue))
331             this.init_stopKeyValue = null;
332
333         // stopSearchOperator init.
334
this.init_stopSearchOperator = stopSearchOperator;
335
336         // reset the "current" position to starting condition.
337
pos.init();
338
339
340         // Verify that all columns in start key value, stop key value, and
341
// qualifiers are present in the list of columns described by the
342
// scanColumnList.
343
if (SanityManager.DEBUG)
344         {
345             if (init_scanColumnList != null)
346             {
347                 // verify that all columns specified in qualifiers, start
348
// and stop positions are specified in the scanColumnList.
349

350                 FormatableBitSet required_cols;
351
352                 if (qualifier != null)
353                     required_cols = RowUtil.getQualifierBitSet(qualifier);
354                 else
355                     required_cols = new FormatableBitSet(0);
356
357                 // add in start columns
358
if (this.init_startKeyValue != null)
359                 {
360                     required_cols.grow(this.init_startKeyValue.length);
361                     for (int i = 0; i < this.init_startKeyValue.length; i++)
362                         required_cols.set(i);
363                 }
364
365                 if (this.init_stopKeyValue != null)
366                 {
367                     required_cols.grow(this.init_stopKeyValue.length);
368                     for (int i = 0; i < this.init_stopKeyValue.length; i++)
369                         required_cols.set(i);
370                 }
371
372                 FormatableBitSet required_cols_and_scan_list =
373                     (FormatableBitSet) required_cols.clone();
374
375                 required_cols_and_scan_list.and(init_scanColumnList);
376
377                 // FormatableBitSet equals requires the two FormatableBitSets to be of same
378
// length.
379
required_cols.grow(init_scanColumnList.size());
380
381                 if (!required_cols_and_scan_list.equals(required_cols))
382                 {
383                     SanityManager.THROWASSERT(
384                         "Some column specified in a Btree " +
385                         " qualifier/start/stop list is " +
386                         "not represented in the scanColumnList." +
387                         "\n:required_cols_and_scan_list = " +
388                             required_cols_and_scan_list +
389                         "\n;required_cols = " + required_cols +
390                         "\n;init_scanColumnList = " + init_scanColumnList);
391                 }
392             }
393         }
394
395         // Scan is fully initialized and ready to go.
396
scan_state = SCAN_INIT;
397     }
398
399
400     /**
401      * Reposition the scan upon entering the fetchRows loop.
402      * <p>
403      * Called upon entering fetchRows() while in the SCAN_INPROGRESS state.
404      * Do work necessary to look at rows in the current page of the scan.
405      * <p>
406      * The default implementation uses a record handle to maintain a scan
407      * position. It will get the latch again on the current
408      * scan position and set the slot to the current record handle.
409      *
410      * @exception StandardException Standard exception policy.
411      **/

412     protected void positionAtResumeScan(
413     RowPosition pos)
414         throws StandardException
415     {
416         if (SanityManager.DEBUG)
417         {
418             SanityManager.ASSERT(
419                 scan_position.current_rh != null, this.toString());
420         }
421
422         // reposition the scan at the row just before the next one to return.
423
// This routine handles the mess of repositioning if the row or the
424
// page has disappeared. This can happen if a lock was not held on the
425
// row while not holding the latch.
426
open_conglom.latchPageAndRepositionScan(scan_position);
427     }
428
429     /**
430      * Move the scan from SCAN_INIT to SCAN_INPROGRESS.
431      * <p>
432      * This routine is called to move the scan from SCAN_INIT to
433      * SCAN_INPROGRESS. Upon return from this routine it is expected
434      * that scan_position is set such that calling the generic
435      * scan loop will reach the first row of the scan. Note that this
436      * usually means setting the scan_postion to one before the 1st
437      * row to be returned.
438      * <p>
439      *
440      * @exception StandardException Standard exception policy.
441      **/

442     protected void positionAtStartForForwardScan(
443     RowPosition pos)
444         throws StandardException
445     {
446         if (pos.current_rh == null)
447         {
448             // 1st positioning of scan (delayed from openScan).
449
pos.current_page =
450                 open_conglom.getContainer().getFirstPage();
451
452             if (SanityManager.DEBUG)
453             {
454                 SanityManager.ASSERT(
455                     pos.current_page.getPageNumber() ==
456                     ContainerHandle.FIRST_PAGE_NUMBER);
457
458                 if (pos.current_page.recordCount() < 1)
459                     SanityManager.THROWASSERT(
460                         "record count = " + pos.current_page.recordCount());
461             }
462
463             // set up for scan to continue at beginning of first page just
464
// after first first control row on first page.
465
pos.current_slot = Page.FIRST_SLOT_NUMBER;
466         }
467         else
468         {
469             // 1st positioning of scan following a reopenScanByRowLocation
470

471             // reposition the scan at the row just before the next one to
472
// return. This routine handles the mess of repositioning if the
473
// row or the page has disappeared. This can happen if a lock was
474
// not held on the row while not holding the latch.
475
open_conglom.latchPageAndRepositionScan(pos);
476
477             // set up for scan to at the specified record handle (position one
478
// before it so that the loop increment and find it).
479
pos.current_slot -= 1;
480         }
481
482         pos.current_rh = null;
483         this.stat_numpages_visited = 1;
484         this.scan_state = SCAN_INPROGRESS;
485     }
486
487     /**
488      * Position scan to slot before first slot on next page.
489      * <p>
490      * @exception StandardException Standard exception policy.
491      **/

492     protected void positionAtNextPage(
493     RowPosition pos)
494         throws StandardException
495     {
496         // The current_page can become null, in a rare multi-user case, where
497
// all pages in the heap are deallocated, in the middle of the scan
498
// loop, when no latches are held, and the scan is waiting on a lock.
499
// In this case the lockPositionForRead code, has nowhere good to
500
// position the scan, so it just sets the page to null and returns.
501
if (pos.current_page != null)
502         {
503             // save current page number.
504
long pageid = pos.current_page.getPageNumber();
505
506             // unlatch old page.
507
pos.unlatch();
508
509             // latch page after current page number.
510
pos.current_page =
511                 open_conglom.getContainer().getNextPage(pageid);
512
513             // set up for scan to continue at beginning of this new page.
514
pos.current_slot = Page.FIRST_SLOT_NUMBER - 1;
515         }
516     }
517
518     /**
519      * Do any necessary work to complete the scan.
520      *
521      * @exception StandardException Standard exception policy.
522      **/

523     protected void positionAtDoneScan(
524     RowPosition pos)
525         throws StandardException
526     {
527         // Unlatch current page if any.
528
pos.unlatch();
529
530         // unlock the previous row.
531
if (scan_position.current_rh != null)
532         {
533             open_conglom.unlockPositionAfterRead(scan_position);
534             scan_position.current_rh = null;
535         }
536
537         this.scan_state = SCAN_DONE;
538     }
539
540     public void reopenScanByRowLocation(
541     RowLocation startRowLocation,
542     Qualifier qualifier[][])
543         throws StandardException
544     {
545         throw StandardException.newException(
546                 SQLState.BTREE_UNIMPLEMENTED_FEATURE);
547     }
548
549     /**************************************************************************
550      * Protected methods of This class:
551      **************************************************************************
552      */

553
554     /**
555      * Create object which represents the scan position.
556      * <p>
557      * Designed so that extending classes can override and allocate
558      * implementation specific row position's.
559      *
560      * @exception StandardException Standard exception policy.
561      **/

562     protected RowPosition allocateScanPosition()
563         throws StandardException
564     {
565         return(new RowPosition());
566     }
567
568     /**
569      * Fetch the next N rows from the table.
570      * <p>
571      * Utility routine used by both fetchSet() and fetchNextGroup().
572      *
573      * @exception StandardException Standard exception policy.
574      **/

575     protected int fetchRows(
576     DataValueDescriptor[][] row_array,
577     RowLocation[] rowloc_array,
578     BackingStoreHashtable hash_table,
579     long max_rowcnt,
580     int[] key_column_numbers)
581         throws StandardException
582     {
583         int ret_row_count = 0;
584         DataValueDescriptor[] fetch_row = null;
585
586         if (max_rowcnt == -1)
587             max_rowcnt = Long.MAX_VALUE;
588
589         if (SanityManager.DEBUG)
590         {
591             if (row_array != null)
592             {
593                 SanityManager.ASSERT(row_array[0] != null,
594                     "first array slot in fetchNextGroup() must be non-null.");
595                 SanityManager.ASSERT(hash_table == null);
596             }
597             else
598             {
599                 SanityManager.ASSERT(hash_table != null);
600             }
601         }
602
603         if (this.scan_state == SCAN_INPROGRESS)
604         {
605             positionAtResumeScan(scan_position);
606         }
607         else if (this.scan_state == SCAN_INIT)
608         {
609             positionAtStartForForwardScan(scan_position);
610
611         }
612         else if (this.scan_state == SCAN_HOLD_INPROGRESS)
613         {
614             reopenAfterEndTransaction();
615
616             if (SanityManager.DEBUG)
617             {
618                 SanityManager.ASSERT(
619                     scan_position.current_rh != null, this.toString());
620             }
621
622             // reposition the scan at the row just before the next one to
623
// return.
624
// This routine handles the mess of repositioning if the row or
625
// the page has disappeared. This can happen if a lock was not
626
// held on the row while not holding the latch.
627
open_conglom.latchPageAndRepositionScan(scan_position);
628
629             this.scan_state = SCAN_INPROGRESS;
630         }
631         else if (this.scan_state == SCAN_HOLD_INIT)
632         {
633             reopenAfterEndTransaction();
634
635             positionAtStartForForwardScan(scan_position);
636
637         }
638         else
639         {
640             if (SanityManager.DEBUG)
641                 SanityManager.ASSERT(this.scan_state == SCAN_DONE);
642
643             return(0);
644         }
645
646         // At this point:
647
// scan_position.current_page is latched.
648
// scan_position.current_slot is the slot on scan_position.current_page
649
// just before the "next" record this routine should process.
650

651         // loop through successive pages and successive slots on those
652
// pages. Stop when either the last page is reached
653
// (scan_position.current_page will be null).
654
// Along the way apply qualifiers to skip rows which don't qualify.
655

656         while (scan_position.current_page != null)
657         {
658             while ((scan_position.current_slot + 1) <
659                     scan_position.current_page.recordCount())
660             {
661                 // unlock the previous row.
662
if (scan_position.current_rh != null)
663                 {
664                     open_conglom.unlockPositionAfterRead(scan_position);
665
666                 }
667                 // Allocate a new row to read the row into.
668
if (fetch_row == null)
669                 {
670                     if (hash_table == null)
671                     {
672                          // point at allocated row in array if one exists.
673
if (row_array[ret_row_count] == null)
674                         {
675                             row_array[ret_row_count] =
676                               open_conglom.getRuntimeMem().get_row_for_export();
677                         }
678
679                         fetch_row = row_array[ret_row_count];
680                     }
681                     else
682                     {
683                         fetch_row =
684                             open_conglom.getRuntimeMem().get_row_for_export();
685                     }
686                 }
687
688                 // move scan current position forward.
689
scan_position.positionAtNextSlot();
690
691                 // Lock the row.
692
boolean lock_granted_while_latch_held =
693                     open_conglom.lockPositionForRead(
694                         scan_position, (RowPosition) null, true, true);
695
696                 if (!lock_granted_while_latch_held)
697                 {
698                     // if lock could not be granted while holding
699
// latch, then the row may either be on the same page
700
// or it may no longer exist, this implementation does not
701
// handle rows which move to different pages.
702
//
703
// If the row moved on the same page then
704
// lockPositionForRead() will have automatically updated
705
// the scan_postion argument to point to it, and we
706
// wil now have a latch and a lock on that row.
707
//
708
// If the row no longer exists then the
709
// "moveForwardIfRowDisappears" argument makes this routine
710
// find the "next" row in the heap and position on it. If
711
// a valid row exists in the current page to position on,
712
// then lockPositionForRead() will position on it, get
713
// a lock on it, and return with a latch on the page.
714
// Otherwise the routine will return with current_slot == -1
715
// and it is up to this routine to continue the scan as
716
// normal at the top of the loop.
717

718                     if (scan_position.current_page == null)
719                     {
720                         // page has been unlatched and the scan is done, there
721
// are no more pages. getNextPage() has been coded to
722
// handle a null current_page.
723

724                         break;
725                     }
726                     else if (scan_position.current_slot == -1)
727                     {
728                         // This means that lockPositionForRead() had to
729
// reposition the scan forward to a new page, because
730
// the row the scan was locking was purged, when the
731
// latch was released to wait on the lock. In this
732
// case just jump back to the top of loop and continue
733
// scan.
734

735                         if (SanityManager.DEBUG)
736                         {
737                             SanityManager.ASSERT(
738                                 scan_position.current_rh == null);
739                         }
740
741                         continue;
742                     }
743                 }
744
745                 this.stat_numrows_visited++;
746
747                 // lockRowAtPosition set pos.current_rh as part of getting lock.
748
if (SanityManager.DEBUG)
749                 {
750                     SanityManager.ASSERT(scan_position.current_rh != null);
751
752                     // make sure current_rh and current_slot are in sync
753
if (scan_position.current_slot !=
754                             scan_position.current_page.getSlotNumber(
755                                 scan_position.current_rh))
756                     {
757                         SanityManager.THROWASSERT(
758                             "current_slot = " + scan_position.current_slot +
759                             "current_rh = " + scan_position.current_rh +
760                             "current_rh.slot = " +
761                             scan_position.current_page.getSlotNumber(
762                                 scan_position.current_rh));
763                     }
764                 }
765
766                 // fetchFromSlot returns null if row does not qualify.
767

768                 scan_position.current_rh_qualified =
769                     (scan_position.current_page.fetchFromSlot(
770                         scan_position.current_rh,
771                         scan_position.current_slot,
772                         fetch_row,
773                         init_fetchDesc,
774                         false) != null);
775
776                 if (scan_position.current_rh_qualified)
777                 {
778                     // qualifying row.
779

780
781                     // scan_position.current_rh is save position of scan while
782
// latch is not held. It currently points at the
783
// scan_position.current_slot in search (while latch is
784
// held).
785
if (SanityManager.DEBUG)
786                     {
787                         // make sure current_rh and current_slot are in sync
788
SanityManager.ASSERT(
789                             scan_position.current_slot ==
790                                 scan_position.current_page.getSlotNumber(
791                                     scan_position.current_rh));
792                     }
793
794                     // Found qualifying row. Done fetching rows for the group?
795
ret_row_count++;
796                     stat_numrows_qualified++;
797
798
799                     if (hash_table == null)
800                     {
801                         if (rowloc_array != null)
802                         {
803                             // if requested return the associated row location.
804
setRowLocationArray(
805                                 rowloc_array, ret_row_count - 1, scan_position);
806                         }
807
808                         fetch_row = null;
809                     }
810                     else
811                     {
812                         if (hash_table.put(false, fetch_row))
813                         {
814                             // The row was inserted into the hash table so we
815
// need to create a new row next time through.
816
fetch_row = null;
817                         }
818                     }
819
820                     if (max_rowcnt <= ret_row_count)
821                     {
822                         // exit fetch row loop and return to the client.
823
scan_position.unlatch();
824
825                         if (SanityManager.DEBUG)
826                         {
827                             SanityManager.ASSERT(
828                                 scan_position.current_rh != null);
829                         }
830
831                         return(ret_row_count);
832                     }
833                 }
834             }
835
836             positionAtNextPage(scan_position);
837
838             this.stat_numpages_visited++;
839         }
840
841         // Reached last page of scan.
842
positionAtDoneScan(scan_position);
843
844         // we need to decrement when we stop scan at the end of the table.
845
this.stat_numpages_visited--;
846
847         return(ret_row_count);
848     }
849
850     /**
851     Reposition the current scan. This call is semantically the same as if
852     the current scan had been closed and a openScan() had been called instead.
853     The scan is reopened against the same conglomerate, and the scan
854     is reopened with the same "scan column list", "hold" and "forUpdate"
855     parameters passed in the original openScan.
856     <p>
857     The statistics gathered by the scan are not reset to 0 by a reopenScan(),
858     rather they continue to accumulate.
859     <p>
860     Note that this operation is currently only supported on Heap conglomerates.
861     Also note that order of rows within are heap are not guaranteed, so for
862     instance positioning at a RowLocation in the "middle" of a heap, then
863     inserting more data, then continuing the scan is not guaranteed to see
864     the new rows - they may be put in the "beginning" of the heap.
865
866     @param startRecordHandle An existing RecordHandle within the conglomerate,
867     at which to position the start of the scan. The scan will begin at this
868     location and continue forward until the end of the conglomerate.
869     Positioning at a non-existent RowLocation (ie. an invalid one or one that
870     had been deleted), will result in an exception being thrown when the
871     first next operation is attempted.
872
873     @param qualifier An array of qualifiers which, applied
874     to each key, restrict the rows returned by the scan. Rows
875     for which any one of the qualifiers returns false are not
876     returned by the scan. If null, all rows are returned.
877
878     @exception StandardException Standard exception policy.
879     **/

880     protected void reopenScanByRecordHandle(
881     RecordHandle startRecordHandle,
882     Qualifier qualifier[][])
883         throws StandardException
884     {
885         // initialize scan position parameters at beginning of scan
886
this.scan_state =
887             (!open_conglom.getHold() ? SCAN_INIT : SCAN_HOLD_INIT);
888
889         // position the scan at the row before the given record id, so that
890
// the first "next" starts on the given row.
891
scan_position.current_rh = startRecordHandle;
892     }
893
894     protected abstract void setRowLocationArray(
895     RowLocation[] rowloc_array,
896     int index,
897     RowPosition pos)
898         throws StandardException
899         ;
900
901     /**************************************************************************
902      * abstract protected Methods of This class:
903      **************************************************************************
904      */

905
906     /**************************************************************************
907      * Public Methods of This class:
908      **************************************************************************
909      */

910     public void init(
911     OpenConglomerate open_conglom,
912     FormatableBitSet scanColumnList,
913     DataValueDescriptor[] startKeyValue,
914     int startSearchOperator,
915     Qualifier qualifier[][],
916     DataValueDescriptor[] stopKeyValue,
917     int stopSearchOperator)
918         throws StandardException
919     {
920         super.init(open_conglom);
921
922         // RESOLVE (mikem) - move this into runtime_mem
923
scan_position = allocateScanPosition();
924
925         // remember inputs
926
init_scanColumnList = scanColumnList;
927
928         positionAtInitScan(
929             startKeyValue,
930             startSearchOperator,
931             qualifier,
932             stopKeyValue,
933             stopSearchOperator,
934             scan_position);
935         
936         reusableRecordIdSequenceNumber =
937                     open_conglom.getContainer().getReusableRecordIdSequenceNumber();
938     }
939
940
941     public final int getNumPagesVisited()
942     {
943         return(stat_numpages_visited);
944     }
945     public final int getNumRowsVisited()
946     {
947         return(stat_numrows_visited);
948     }
949     public final int getNumRowsQualified()
950     {
951         return(stat_numrows_qualified);
952     }
953     public final FormatableBitSet getScanColumnList()
954     {
955         return(init_scanColumnList);
956     }
957     public final DataValueDescriptor[] getStartKeyValue()
958     {
959         return(init_startKeyValue);
960     }
961     public final int getStartSearchOperator()
962     {
963         return(init_startSearchOperator);
964     }
965     public final DataValueDescriptor[] getStopKeyValue()
966     {
967         return(init_stopKeyValue);
968     }
969     public final int getStopSearchOperator()
970     {
971         return(init_stopSearchOperator);
972     }
973     public final Qualifier[][] getQualifier()
974     {
975         return(init_qualifier);
976     }
977
978
979     public final int getScanState()
980     {
981         return(scan_state);
982     }
983     public final void setScanState(int state)
984     {
985         scan_state = state;
986     }
987     public final RowPosition getScanPosition()
988     {
989         return(scan_position);
990     }
991     public final void setScanPosition(RowPosition pos)
992     {
993         scan_position = pos;
994     }
995
996     /**************************************************************************
997      * Public Methods implementing ScanController:
998      **************************************************************************
999      */

1000    private void closeScan()
1001        throws StandardException
1002    {
1003        super.close();
1004
1005        // If we are closed due to catching an error in the middle of init,
1006
// xact_manager may not be set yet.
1007
if (open_conglom.getXactMgr() != null)
1008            open_conglom.getXactMgr().closeMe(this);
1009
1010        // help the garbage collector.
1011
this.init_qualifier = null;
1012        init_scanColumnList = null;
1013        init_startKeyValue = null;
1014        init_stopKeyValue = null;
1015    }
1016
1017    public void close()
1018        throws StandardException
1019    {
1020        // Finish the scan - this may release locks if read committed and scan
1021
// still holds some locks, and close comes before scan.next() returned
1022
// that scan was done.
1023
positionAtDoneScan(scan_position);
1024
1025        closeScan();
1026    }
1027
1028    /**
1029     * Reopens the scan after it has been closed as part of a commit.
1030     * This method will check the reusableRecordIdSequenceNumber of the
1031     * container, and will set the rowLocationsInvalidated flag if it has
1032     * changed.
1033     * @return true if the conglomerate has been reopened
1034     * @exception StandardException Derby standard exception
1035     */

1036    protected final boolean reopenAfterEndTransaction()
1037        throws StandardException
1038    {
1039        // Only reopen if holdable
1040
if (!open_conglom.getHold())
1041        {
1042            return(false);
1043        }
1044        
1045        ContainerHandle container = open_conglom.reopen();
1046        switch (scan_state) {
1047        case SCAN_INPROGRESS:
1048        case SCAN_HOLD_INPROGRESS:
1049        case SCAN_DONE:
1050            if (container.getReusableRecordIdSequenceNumber() !=
1051                reusableRecordIdSequenceNumber)
1052            {
1053                rowLocationsInvalidated = true;
1054            }
1055            break;
1056        case SCAN_INIT:
1057        case SCAN_HOLD_INIT:
1058            reusableRecordIdSequenceNumber =
1059                container.getReusableRecordIdSequenceNumber();
1060            break;
1061        default:
1062            break;
1063        }
1064        return(true);
1065    }
1066
1067    public boolean closeForEndTransaction(
1068    boolean closeHeldScan)
1069        throws StandardException
1070    {
1071        if ((!open_conglom.getHold()) || closeHeldScan)
1072        {
1073            // close the scan as part of the commit/abort
1074

1075            this.scan_state = SCAN_DONE;
1076
1077            closeScan();
1078
1079            return(true);
1080        }
1081        else
1082        {
1083            super.close();
1084
1085            // allow the scan to continue after the commit.
1086
// locks and latches will be released as part of the commit, so
1087
// no need to release them by hand.
1088

1089            if (this.scan_state == SCAN_INPROGRESS)
1090                this.scan_state = SCAN_HOLD_INPROGRESS;
1091            else if (this.scan_state == SCAN_INIT)
1092                this.scan_state = SCAN_HOLD_INIT;
1093
1094
1095            return(false);
1096        }
1097    }
1098
1099
1100    /**
1101    @see ScanController#delete
1102    **/

1103    public boolean delete()
1104        throws StandardException
1105    {
1106        repositionScanForUpateOper();
1107
1108        boolean ret_val = true;
1109
1110        // RESOLVE (mikem) - RECID - performance could be better if we did not
1111
// have to call isDeletedAtSlot().
1112

1113        // RESOLVE (mikem) - share code below with conglomerateController.
1114

1115        if (scan_position.current_page.isDeletedAtSlot(
1116                scan_position.current_slot))
1117        {
1118            ret_val = false;
1119        }
1120        else
1121        {
1122            // Delete the row
1123
scan_position.current_page.deleteAtSlot(
1124                scan_position.current_slot, true, (LogicalUndo) null);
1125
1126            if (scan_position.current_page.nonDeletedRecordCount() == 0)
1127            {
1128                queueDeletePostCommitWork(scan_position);
1129            }
1130        }
1131
1132        scan_position.unlatch();
1133
1134        return(ret_val);
1135    }
1136
1137
1138    /**
1139     * A call to allow client to indicate that current row does not qualify.
1140     * <p>
1141     * Indicates to the ScanController that the current row does not
1142     * qualify for the scan. If the isolation level of the scan allows,
1143     * this may result in the scan releasing the lock on this row.
1144     * <p>
1145     * Note that some scan implimentations may not support releasing locks on
1146     * non-qualifying rows, or may delay releasing the lock until sometime
1147     * later in the scan (ie. it may be necessary to keep the lock until
1148     * either the scan is repositioned on the next row or page).
1149     * <p>
1150     * This call should only be made while the scan is positioned on a current
1151     * valid row.
1152     * RESOLVE (mikem-05/29/98) - Implement this when we support levels of
1153     * concurrency less than serializable.
1154     *
1155     * @exception StandardException Standard exception policy.
1156     **/

1157    public void didNotQualify()
1158        throws StandardException
1159    {
1160    }
1161
1162    /**
1163     * Insert all rows that qualify for the current scan into the input
1164     * Hash table.
1165     * <p>
1166     * This routine scans executes the entire scan as described in the
1167     * openScan call. For every qualifying unique row value an entry is
1168     * placed into the HashTable. For unique row values the entry in the
1169     * Hashtable has a key value of the object stored in
1170     * row[key_column_number], and the value of the data is row. For row
1171     * values with duplicates, the key value is also row[key_column_number],
1172     * but the value of the data is a Vector of
1173     * rows. The caller will have to call "instanceof" on the data value
1174     * object if duplicates are expected, to determine if the data value
1175     * of the Hashtable entry is a row or is a Vector of rows.
1176     * <p>
1177     * Note, that for this routine to work efficiently the caller must
1178     * ensure that the object in row[key_column_number] implements
1179     * the hashCode and equals method as appropriate for it's datatype.
1180     * <p>
1181     * It is expected that this call will be the first and only call made in
1182     * an openscan. Qualifiers and stop position of the openscan are applied
1183     * just as in a normal scan. This call is logically equivalent to the
1184     * caller performing the following:
1185     *
1186     * import java.util.Hashtable;
1187     *
1188     * hash_table = new Hashtable();
1189     *
1190     * while (next())
1191     * {
1192     * row = create_new_row();
1193     * fetch(row);
1194     * if ((duplicate_value =
1195     * hash_table.put(row[key_column_number], row)) != null)
1196     * {
1197     * Vector row_vec;
1198     *
1199     * // inserted a duplicate
1200     * if ((duplicate_value instanceof vector))
1201     * {
1202     * row_vec = (Vector) duplicate_value;
1203     * }
1204     * else
1205     * {
1206     * // allocate vector to hold duplicates
1207     * row_vec = new Vector(2);
1208     *
1209     * // insert original row into vector
1210     * row_vec.addElement(duplicate_value);
1211     *
1212     * // put the vector as the data rather than the row
1213     * hash_table.put(row[key_column_number], row_vec);
1214     * }
1215     *
1216     * // insert new row into vector
1217     * row_vec.addElement(row);
1218     * }
1219     * }
1220     * <p>
1221     * The columns of the row will be the standard columns returned as
1222     * part of a scan, as described by the validColumns - see openScan for
1223     * description.
1224     * RESOLVE - is this ok? or should I hard code somehow the row to
1225     * be the first column and the row location?
1226     * <p>
1227     * Currently it is only possible to hash on the first column in the
1228     * conglomerate, in the future we may change the interface to allow
1229     * hashing either on a different column or maybe on a combination of
1230     * columns.
1231     * <p>
1232     * No overflow to external storage is provided, so calling this routine
1233     * on a 1 gigabyte conglomerate will incur at least 1 gigabyte of memory
1234     * (probably failing with a java out of memory condition). If this
1235     * routine gets an out of memory condition, or if "max_rowcnt" is
1236     * exceeded then then the routine will give up, empty the Hashtable,
1237     * and return "false."
1238     * <p>
1239     * On exit from this routine, whether the fetchSet() succeeded or not
1240     * the scan is complete, it is positioned just the same as if the scan
1241     * had been drained by calling "next()" until it returns false (ie.
1242     * fetchNext() and next() calls will return false).
1243     * reopenScan() can be called to restart the scan.
1244     * <p>
1245     *
1246     * RESOLVE - until we get row counts what should we do for sizing the
1247     * the size, capasity, and load factor of the hash table.
1248     * For now it is up to the caller to create the Hashtable,
1249     * Access does not reset any parameters.
1250     * <p>
1251     * RESOLVE - I am not sure if access should be in charge of allocating
1252     * the new row objects. I know that I can do this in the
1253     * case of btree's, but I don't think I can do this in heaps.
1254     * Maybe this is solved by work to be done on the sort
1255     * interface.
1256     *
1257     *
1258     * @param max_rowcnt The maximum number of rows to insert into the
1259     * Hash table. Pass in -1 if there is no maximum.
1260     * @param key_column_numbers The column numbers of the columns in the
1261     * scan result row to be the key to the Hashtable.
1262     * "0" is the first column in the scan result
1263     * row (which may be different than the first
1264     * column in the row in the table of the scan).
1265     * @param hash_table The java HashTable to load into.
1266     *
1267     * @exception StandardException Standard exception policy.
1268     **/

1269    public void fetchSet(
1270    long max_rowcnt,
1271    int[] key_column_numbers,
1272    BackingStoreHashtable hash_table)
1273        throws StandardException
1274    {
1275        fetchRows(
1276            (DataValueDescriptor[][]) null,
1277            (RowLocation[]) null,
1278            hash_table,
1279            max_rowcnt,
1280            key_column_numbers);
1281
1282        return;
1283    }
1284
1285    /**
1286    Reposition the current scan. This call is semantically the same as if
1287    the current scan had been closed and a openScan() had been called instead.
1288    The scan is reopened with against the same conglomerate, and the scan
1289    is reopened with the same "hold" and "forUpdate" parameters passed in
1290    the original openScan. The previous template row continues to be used.
1291
1292    @param startKeyValue An indexable row which holds a
1293    (partial) key value which, in combination with the
1294    startSearchOperator, defines the starting position of
1295    the scan. If null, the starting position of the scan
1296    is the first row of the conglomerate.
1297    
1298    @param startSearchOperator an operator which defines
1299    how the startKeyValue is to be searched for. If
1300    startSearchOperator is ScanController.GE, the scan starts on
1301    the first row which is greater than or equal to the
1302    startKeyValue. If startSearchOperation is ScanController.GT,
1303    the scan starts on the first row whose key is greater than
1304    startKeyValue. The startSearchOperation parameter is
1305    ignored if the startKeyValue parameter is null.
1306
1307    @param qualifier An array of qualifiers which, applied
1308    to each key, restrict the rows returned by the scan. Rows
1309    for which any one of the qualifiers returns false are not
1310    returned by the scan. If null, all rows are returned.
1311
1312    @param stopKeyValue An indexable row which holds a
1313    (partial) key value which, in combination with the
1314    stopSearchOperator, defines the ending position of
1315    the scan. If null, the ending position of the scan
1316    is the last row of the conglomerate.
1317    
1318    @param stopSearchOperator an operator which defines
1319    how the stopKeyValue is used to determine the scan stopping
1320    position. If stopSearchOperation is ScanController.GE, the scan
1321    stops just before the first row which is greater than or
1322    equal to the stopKeyValue. If stopSearchOperation is
1323    ScanController.GT, the scan stops just before the first row whose
1324    key is greater than startKeyValue. The stopSearchOperation
1325    parameter is ignored if the stopKeyValue parameter is null.
1326
1327    @exception StandardException Standard exception policy.
1328    **/

1329    public void reopenScan(
1330    DataValueDescriptor[] startKeyValue,
1331    int startSearchOperator,
1332    Qualifier qualifier[][],
1333    DataValueDescriptor[] stopKeyValue,
1334    int stopSearchOperator)
1335        throws StandardException
1336    {
1337        if (SanityManager.DEBUG)
1338        {
1339            if (!open_conglom.getHold())
1340            {
1341                SanityManager.ASSERT(
1342                    !open_conglom.isClosed(),
1343                    "GenericScanController.reopenScan() called on a non-held closed scan.");
1344            }
1345        }
1346
1347        // initialize scan position parameters at beginning of scan
1348
this.scan_state =
1349            (!open_conglom.getHold() ? SCAN_INIT : SCAN_HOLD_INIT);
1350
1351        scan_position.current_rh = null;
1352    }
1353
1354    /**
1355    @see ScanController#replace
1356    **/

1357    public boolean replace(
1358    DataValueDescriptor[] row,
1359    FormatableBitSet validColumns)
1360        throws StandardException
1361    {
1362        repositionScanForUpateOper();
1363
1364        boolean ret_val =
1365            scan_position.current_page.update(
1366                scan_position.current_rh, row, validColumns);
1367
1368        scan_position.unlatch();
1369
1370        return(ret_val);
1371    }
1372
1373    /**
1374    Returns true if the current position of the scan still qualifies
1375    under the set of qualifiers passed to the openScan(). When called
1376    this routine will reapply all qualifiers against the row currently
1377    positioned and return true if the row still qualifies. If the row
1378    has been deleted or no longer passes the qualifiers then this routine
1379    will return false.
1380    
1381    This case can come about if the current scan
1382    or another scan on the same table in the same transaction
1383    deleted the row or changed columns referenced by the qualifier after
1384    the next() call which positioned the scan at this row.
1385
1386    Note that for comglomerates which don't support update, like btree's,
1387    there is no need to recheck the qualifiers.
1388
1389    The results of a fetch() performed on a scan positioned on
1390    a deleted row are undefined.
1391
1392    @exception StandardException Standard exception policy.
1393    **/

1394    public boolean doesCurrentPositionQualify()
1395        throws StandardException
1396    {
1397        if (scan_state != SCAN_INPROGRESS)
1398            throw StandardException.newException(
1399                    SQLState.AM_SCAN_NOT_POSITIONED);
1400
1401        if (!open_conglom.latchPage(scan_position))
1402        {
1403            return(false);
1404        }
1405
1406        DataValueDescriptor row[] =
1407            open_conglom.getRuntimeMem().get_scratch_row();
1408
1409        // If fetchFromSlot returns null it either means the row is deleted,
1410
// or the qualifier evaluates to false.
1411

1412        boolean ret_val =
1413            (scan_position.current_page.fetchFromSlot(
1414                scan_position.current_rh,
1415                scan_position.current_slot,
1416                row,
1417                init_fetchDesc,
1418                false) != null);
1419
1420        scan_position.unlatch();
1421
1422        return(ret_val);
1423    }
1424
1425    /**
1426    Fetch the row at the current position of the Scan without applying the
1427    qualifiers.
1428    
1429    @see ScanController#fetchWithoutQualify
1430    **/

1431    public void fetchWithoutQualify(DataValueDescriptor[] row)
1432        throws StandardException
1433    {
1434        fetch(row, false);
1435    }
1436    
1437    /**
1438    Fetch the row at the current position of the Scan.
1439
1440    @see ScanController#fetch
1441    **/

1442    public void fetch(DataValueDescriptor[] row)
1443        throws StandardException
1444    {
1445        fetch(row, true);
1446    }
1447
1448    /**
1449    Fetch the row at the current position of the Scan.
1450
1451    @param row The row into which the value of the current
1452     position in the scan is to be stored.
1453
1454    @param qualify Indicates whether the qualifiers should be applied.
1455
1456    @exception StandardException Standard exception policy.
1457    **/

1458    private void fetch(DataValueDescriptor[] row, boolean qualify)
1459        throws StandardException
1460    {
1461        if (scan_state != SCAN_INPROGRESS)
1462            throw StandardException.newException(
1463                    SQLState.AM_SCAN_NOT_POSITIONED);
1464
1465        if (!open_conglom.latchPage(scan_position))
1466        {
1467            throw StandardException.newException(
1468                    SQLState.AM_RECORD_NOT_FOUND,
1469                    open_conglom.getContainer().getId(),
1470                    new Long JavaDoc(scan_position.current_rh.getId()));
1471        }
1472
1473        // RESOLVE (mikem) - should this call apply the qualifiers again?
1474
RecordHandle rh =
1475            scan_position.current_page.fetchFromSlot(
1476                scan_position.current_rh,
1477                scan_position.current_slot,
1478                row,
1479                qualify ? init_fetchDesc : null,
1480                false);
1481
1482        scan_position.unlatch();
1483
1484        if (rh == null)
1485        {
1486            /*
1487            if (SanityManager.DEBUG)
1488            {
1489                if (isCurrentPositionDeleted())
1490                    SanityManager.THROWASSERT(
1491                        "The record (" +
1492                        open_conglom.getContainer().getId() +
1493                        ", " +
1494                        scan_position.current_rh.getPageNumber() + ", " +
1495                        scan_position.current_rh.getId() + ") " +
1496                        "being fetched is marked deleted on page.:\n");
1497            }
1498            */

1499
1500            throw StandardException.newException(
1501                    SQLState.AM_RECORD_NOT_FOUND,
1502                    open_conglom.getContainer().getId(),
1503                    new Long JavaDoc(scan_position.current_rh.getId()));
1504        }
1505
1506        return;
1507    }
1508
1509    /**
1510    Fetch the location of the current position in the scan.
1511    @see ScanController#fetchLocation
1512
1513    @exception StandardException Standard exception policy.
1514    **/

1515    public void fetchLocation(RowLocation templateLocation)
1516        throws StandardException
1517    {
1518        throw StandardException.newException(
1519                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1520    }
1521
1522    /**
1523     * Return ScanInfo object which describes performance of scan.
1524     * <p>
1525     * Return ScanInfo object which contains information about the current
1526     * scan.
1527     * <p>
1528     *
1529     * @see ScanInfo
1530     *
1531     * @return The ScanInfo object which contains info about current scan.
1532     *
1533     * @exception StandardException Standard exception policy.
1534     **/

1535    public ScanInfo getScanInfo()
1536        throws StandardException
1537    {
1538        throw StandardException.newException(
1539                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1540    }
1541
1542
1543
1544    /**
1545    Returns true if the current position of the scan is at a
1546    deleted row. This case can come about if the current scan
1547    or another scan on the same table in the same transaction
1548    deleted the row after the next() call which positioned the
1549    scan at this row.
1550
1551    The results of a fetch() performed on a scan positioned on
1552    a deleted row are undefined.
1553
1554    @exception StandardException Standard exception policy.
1555    **/

1556    public boolean isCurrentPositionDeleted()
1557        throws StandardException
1558    {
1559        if (scan_state != SCAN_INPROGRESS)
1560            throw StandardException.newException(
1561                    SQLState.AM_SCAN_NOT_POSITIONED);
1562
1563        if (!open_conglom.latchPage(scan_position))
1564        {
1565            return(true);
1566        }
1567
1568        boolean ret_val =
1569            scan_position.current_page.isDeletedAtSlot(
1570                scan_position.current_slot);
1571
1572        scan_position.unlatch();
1573
1574        return(ret_val);
1575    }
1576}
1577
Popular Tags