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

Quick Search    Search Deep

Source code: org/apache/derby/impl/store/access/conglomerate/GenericScanController.java


1   /*
2   
3      Derby - Class org.apache.derby.impl.store.access.conglomerate.GenericScanController
4   
5      Copyright 2000, 2004 The Apache Software Foundation or its licensors, as applicable.
6   
7      Licensed under the Apache License, Version 2.0 (the "License");
8      you may not use this file except in compliance with the License.
9      You may obtain a copy of the License at
10  
11        http://www.apache.org/licenses/LICENSE-2.0
12  
13     Unless required by applicable law or agreed to in writing, software
14     distributed under the License is distributed on an "AS IS" BASIS,
15     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16     See the License for the specific language governing permissions and
17     limitations under the License.
18  
19   */
20  
21  package org.apache.derby.impl.store.access.conglomerate;
22  
23  import org.apache.derby.iapi.reference.SQLState;
24  
25  import org.apache.derby.iapi.services.sanity.SanityManager;
26  
27  import org.apache.derby.iapi.error.StandardException; 
28  
29  import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;
30  import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
31  import org.apache.derby.iapi.store.access.conglomerate.ScanManager;
32  import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
33  
34  import org.apache.derby.iapi.store.access.ConglomerateController;
35  import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
36  import org.apache.derby.iapi.store.access.Qualifier;
37  import org.apache.derby.iapi.store.access.RowUtil;
38  import org.apache.derby.iapi.store.access.ScanController;
39  import org.apache.derby.iapi.store.access.ScanInfo;
40  import org.apache.derby.iapi.store.access.SpaceInfo;
41  
42  import org.apache.derby.iapi.store.raw.ContainerHandle;
43  import org.apache.derby.iapi.store.raw.FetchDescriptor;
44  import org.apache.derby.iapi.store.raw.Page;
45  import org.apache.derby.iapi.store.raw.RecordHandle;
46  import org.apache.derby.iapi.store.raw.Transaction;
47  
48  import org.apache.derby.iapi.store.access.Qualifier;
49  
50  import org.apache.derby.iapi.types.DataValueDescriptor;
51  
52  import org.apache.derby.iapi.types.Orderable;
53  import org.apache.derby.iapi.types.RowLocation;
54  
55  
56  import org.apache.derby.iapi.store.access.BackingStoreHashtable;
57  import org.apache.derby.iapi.services.io.FormatableBitSet;
58  
59  import java.util.Properties; 
60  
61  
62  /**
63  Generic class implementing shared ScanController methods.
64  
65  Logically a scancontroller is used to scan a set of rows that meet some 
66  specified qualification.  Rows that meet the qualification may be operated
67  upon by the scan to fetch, delete, or replace.  The ScanController also
68  supports the notion or "repositioning" the scan, which simply resets the
69  beginning of the scan to a new place, and allows the user to continue from
70  there.
71  
72  This class attempts to abstract out some of the parts of the scan such that
73  maybe multiple access methods can share code, even if they perform parts of
74  the scan wildly differently.  Here is how the scan has been broken apart:
75  
76  scan_position - this variable holds the current scan position, it may be 
77                  extended
78                  to provide more information if necessary.
79  
80  scan_state    - a scan has 3 possible states: 
81                  SCAN_INIT, SCAN_INPROGRESS, SCAN_DONE
82  
83  positionAtInitScan()
84                - This routine is called to move the scan to the SCAN_INIT state.
85                  It is used both for initialization of the ScanController and
86                  by reopenScan().
87  
88  positionAtStartForForwardScan()
89                - This routine is called to move the scan from SCAN_INIT to 
90                  SCAN_INPROGRESS.  Upon return from this routine it is expected
91                  that scan_position is set such that calling the generic 
92                  scan loop will reach the first row of the scan.  Note that this
93                  usually means setting the scan_postion to one before the 1st 
94                  row to be returned.
95  
96  fetchRows()   - This routine is the meat of the scan, it moves the scan to the
97                  next row, applies necessary qualifiers, and handles group or
98                  non-group operations.  It moves through rows on a page in
99                  order and then moves to the "next" page.
100 
101 positionAtNextPage()
102               - This routine handles moving the scan from the current 
103                 scan_position to the next page.
104 
105 positionAtDoneScan()
106               - Handle all cleanup associated with moving the scan state from
107                 SCAN_INPROGRESS to SCAN_DONE.  This may include releasing locks,
108                 and setting the state of the scan.  This does not close the 
109                 scan, it allows for a reopenScan() to be called.
110 **/
111 
112 public abstract class GenericScanController 
113     extends GenericController implements ScanManager
114 {
115 
116     /**************************************************************************
117      * Constants of the class
118      **************************************************************************
119      */
120 
121     /*
122      * There are 5 states a scan can be in.
123      *     SCAN_INIT - A scan has started but no positioning has been done.
124      *                 The scan will be positioned when the first next() call
125      *                 has been made.  None of the positioning state variables
126      *                 are valid in this state.
127      *     SCAN_INPROGRESS -
128      *                 A scan is in this state after the first next() call.
129      *                 On exit from any GenericScanController method, while in 
130      *                 this state,
131      *                 the scan "points" at a row which qualifies for the 
132      *                 scan.  While not maintaining latches on a page the 
133      *                 current position of the scan is either kept by record
134      *                 handle or key.  To tell which use the following:
135      *                 if (record key == null)
136      *                    record handle has current position
137      *                 else
138      *                    record key has current position
139      *
140      *     SCAN_DONE - Once the end of the table or the stop condition is met
141      *                 then the scan is placed in this state.  Only valid 
142      *                 ScanController method at this point is close().
143      *
144      *     SCAN_HOLD_INIT -
145      *                 The scan has been opened and held open across a commit,
146      *                 at the last commit the state was SCAN_INIT.
147      *                 The scan has never progressed from the SCAN_INIT state
148      *                 during a transaction.  When a next is done the state
149      *                 will either progress to SCAN_INPROGRESS or SCAN_DONE.
150      *
151      *     SCAN_HOLD_INPROGRESS -
152      *                 The scan has been opened and held open across a commit,
153      *                 at the last commit the state was in SCAN_INPROGRESS.
154      *                 The transaction which opened the scan has committed,
155      *                 but the scan was opened with the "hold" option true.
156      *                 At commit the locks were released and the "current"
157      *                 position is remembered.  In this state only two calls
158      *                 are valid, either next() or close().  When next() is
159      *                 called the scan is reopened, the underlying container
160      *                 is opened thus associating all new locks with the current
161      *                 transaction, and the scan continues at the "next" row.
162      */
163     public static final int    SCAN_INIT             = 1;
164     public static final int    SCAN_INPROGRESS       = 2;
165     public static final int    SCAN_DONE             = 3;
166     public static final int    SCAN_HOLD_INIT        = 4;
167     public static final int    SCAN_HOLD_INPROGRESS  = 5;
168 
169     /**************************************************************************
170      * Fields of the class
171      **************************************************************************
172      */
173 
174     /**
175      * The following group of fields are all basic input parameters which are
176      * provided by the calling code when doing a scan.
177      * These are just saved values from what was initially input.
178      **/
179   private FormatableBitSet                 init_scanColumnList;
180     private DataValueDescriptor[]   init_startKeyValue;
181     private int                     init_startSearchOperator;
182     private Qualifier[][]           init_qualifier;
183     private DataValueDescriptor[]   init_stopKeyValue;
184     private int                     init_stopSearchOperator;
185 
186     private FetchDescriptor init_fetchDesc;
187 
188     /**
189      * Delay positioning the table at the start position until the first
190      * next() call.
191      */
192     private int         scan_state;
193 
194     
195     /**
196      * The position for the current scan.  The can be maintained in any
197      * of the following ways:
198      *     record handle - scan_position.current_rh:
199      *         The scan maintains it's position using the record handle while
200      *         it does not have a latch on the page, which is the case anytime
201      *         control leaves access.  The access method must take appropriate
202      *         steps to make sure the record handle will still be valid when
203      *         the scan needs to reposition using the record handle.
204      *     slot number   - scan_position.current_slot:
205      *         While the scan has a latch on the page the scan is positioned
206      *         using the slot number as the order of the rows cannot change
207      *         while the latch is held (unless the holder of the latch causes
208      *         them to move).  
209      *     page number   - (RESOLVE - TODO)
210      *         Sometimes it would be interesting to position a scan "between"
211      *         pages, such that the next time the scan starts is starts at
212      *         the next page.  This would allow us to efficiently do group
213      *         scans returning page at atime results.  
214      *         NOT IMPLEMENTED CURRENTLY.
215      **/
216     protected RowPosition         scan_position;
217 
218     /**
219      * Performance counters ...
220      */
221     protected int stat_numpages_visited         = 0;
222     protected int stat_numrows_visited          = 0;
223     protected int stat_numrows_qualified        = 0;
224 
225     /**************************************************************************
226      * Constructors for This class:
227      **************************************************************************
228      */
229 
230     /**************************************************************************
231      * Private methods of This class:
232      **************************************************************************
233      */
234 
235     private final void repositionScanForUpateOper()
236     throws StandardException
237     {
238         if (scan_state != SCAN_INPROGRESS)
239             throw StandardException.newException(
240                     SQLState.AM_SCAN_NOT_POSITIONED);
241 
242 
243         if (!open_conglom.latchPage(scan_position))
244         {
245             throw StandardException.newException(
246                     SQLState.AM_RECORD_NOT_FOUND, 
247                     open_conglom.getContainer().getId(),
248                     new Long(scan_position.current_rh.getId()));
249         }
250 
251         if (open_conglom.isUseUpdateLocks())
252         {
253             // we only have an U lock at this point which was acquired when the
254             // scan positioned on the row, need to request an
255             // X lock before we can actually perform the delete
256 
257             open_conglom.lockPositionForWrite(
258                 scan_position, false /* not insert */, true);
259         }
260     }
261 
262 
263     /**************************************************************************
264      * Protected methods implementing mechanics of scanning rows:
265      *
266      *     positionAtInitScan()             - move scan state to SCAN_INIT
267      *     positionAtStartForForwardScan()  - SCAN_INIT -> SCAN_INPROGRESS
268      *     positionAtResumeScan()           - reposition after losing scan latch
269      *     fetchRows()                      - move scan while in SCAN_INPROGRESS
270      *     positionAtNextPage()             - move page while in SCAN_INPROGRESS
271      *     positionAtDoneScan()             - SCAN_INPROGRESS -> SCAN_DONE
272      *
273      **************************************************************************
274      */
275 
276     /**
277      * Move scan to the the SCAN_INIT state.
278      * <p>
279      * This routine is called to move the scan to the SCAN_INIT state.
280      * It is used both for initialization of the ScanController and
281      * by reopenScan().
282      **/
283   protected void positionAtInitScan(
284     DataValueDescriptor[]   startKeyValue,
285     int                     startSearchOperator,
286     Qualifier               qualifier[][],
287     DataValueDescriptor[]   stopKeyValue,
288     int                     stopSearchOperator,
289     RowPosition             pos)
290         throws StandardException
291     {
292         // startKeyValue init.
293       this.init_startKeyValue         = startKeyValue;
294     if (RowUtil.isRowEmpty(this.init_startKeyValue, (FormatableBitSet) null))
295       this.init_startKeyValue = null;
296 
297         // startSearchOperator init.
298       this.init_startSearchOperator   = startSearchOperator;
299 
300         // qualifier init.
301         if ((qualifier != null) && (qualifier .length == 0))
302             qualifier = null;
303         this.init_qualifier             = qualifier;
304 
305         // TODO (mikem) - this could be more efficient, by writing
306         // code to figure out length of row, but scratch row is cached
307         // so allocating it here is probably not that bad.
308         init_fetchDesc = 
309             new FetchDescriptor(
310               (open_conglom.getRuntimeMem().get_scratch_row()).length,
311               init_scanColumnList,
312               init_qualifier);
313 
314         // stopKeyValue init.
315       this.init_stopKeyValue          = stopKeyValue;
316         if (RowUtil.isRowEmpty(this.init_stopKeyValue, (FormatableBitSet) null))
317             this.init_stopKeyValue = null;
318 
319         // stopSearchOperator init.
320       this.init_stopSearchOperator    = stopSearchOperator;
321 
322         // reset the "current" position to starting condition.
323         pos.init();
324 
325 
326         // Verify that all columns in start key value, stop key value, and
327         // qualifiers are present in the list of columns described by the
328         // scanColumnList.
329         if (SanityManager.DEBUG)
330         {
331             if (init_scanColumnList != null)
332             {
333                 // verify that all columns specified in qualifiers, start
334                 // and stop positions are specified in the scanColumnList.  
335                 
336                 FormatableBitSet required_cols;
337 
338                 if (qualifier != null)
339                     required_cols = RowUtil.getQualifierBitSet(qualifier);
340                 else
341                     required_cols = new FormatableBitSet(0);
342 
343                 // add in start columns
344                 if (this.init_startKeyValue != null)
345                 {
346           required_cols.grow(this.init_startKeyValue.length);
347                     for (int i = 0; i < this.init_startKeyValue.length; i++)
348                         required_cols.set(i);
349                 }
350 
351                 if (this.init_stopKeyValue != null)
352                 {
353           required_cols.grow(this.init_stopKeyValue.length);
354                     for (int i = 0; i < this.init_stopKeyValue.length; i++)
355                         required_cols.set(i);
356                 }
357 
358                 FormatableBitSet required_cols_and_scan_list = 
359                     (FormatableBitSet) required_cols.clone();
360 
361                 required_cols_and_scan_list.and(init_scanColumnList);
362 
363         // FormatableBitSet equals requires the two FormatableBitSets to be of same
364         // length.
365         required_cols.grow(init_scanColumnList.size());
366 
367                 if (!required_cols_and_scan_list.equals(required_cols))
368                 {
369                     SanityManager.THROWASSERT(
370                         "Some column specified in a Btree " +
371                         " qualifier/start/stop list is " +
372                         "not represented in the scanColumnList." +
373                         "\n:required_cols_and_scan_list = " + 
374                             required_cols_and_scan_list + 
375                         "\n;required_cols = " + required_cols +
376                         "\n;init_scanColumnList = " + init_scanColumnList);
377                 }
378             }
379     } 
380 
381         // Scan is fully initialized and ready to go.
382         scan_state = SCAN_INIT;
383     }
384 
385 
386     /**
387      * Reposition the scan upon entering the fetchRows loop.
388      * <p>
389      * Called upon entering fetchRows() while in the SCAN_INPROGRESS state.
390      * Do work necessary to look at rows in the current page of the scan.
391      * <p>
392      * The default implementation uses a record handle to maintain a scan
393      * position.  It will get the latch again on the current
394      * scan position and set the slot to the current record handle.
395      *
396    * @exception  StandardException  Standard exception policy.
397      **/
398     protected void positionAtResumeScan(
399     RowPosition pos)
400     throws StandardException
401     {
402         if (SanityManager.DEBUG)
403         {
404             SanityManager.ASSERT(
405                 scan_position.current_rh != null, this.toString()); 
406         }
407 
408         // reposition the scan at the row just before the next one to return.
409         // This routine handles the mess of repositioning if the row or the
410         // page has disappeared. This can happen if a lock was not held on the
411         // row while not holding the latch.
412         open_conglom.latchPageAndRepositionScan(scan_position);
413     }
414 
415     /**
416      * Move the scan from SCAN_INIT to SCAN_INPROGRESS.
417      * <p>
418      * This routine is called to move the scan from SCAN_INIT to 
419      * SCAN_INPROGRESS.  Upon return from this routine it is expected
420      * that scan_position is set such that calling the generic 
421      * scan loop will reach the first row of the scan.  Note that this
422      * usually means setting the scan_postion to one before the 1st 
423      * row to be returned.
424      * <p>
425      *
426    * @exception  StandardException  Standard exception policy.
427      **/
428     protected void positionAtStartForForwardScan(
429     RowPosition pos)
430         throws StandardException
431     {
432         if (pos.current_rh == null)
433         {
434             // 1st positioning of scan (delayed from openScan).
435             pos.current_page = 
436                 open_conglom.getContainer().getFirstPage();
437 
438             if (SanityManager.DEBUG)
439             {
440                 SanityManager.ASSERT(
441                     pos.current_page.getPageNumber() == 
442                     ContainerHandle.FIRST_PAGE_NUMBER);
443 
444                 if (pos.current_page.recordCount() < 1)
445                     SanityManager.THROWASSERT(
446                         "record count = " + pos.current_page.recordCount());
447             }
448 
449             // set up for scan to continue at beginning of first page just
450             // after first first control row on first page.
451             pos.current_slot = Page.FIRST_SLOT_NUMBER;
452         }
453         else
454         {
455             // 1st positioning of scan following a reopenScanByRowLocation
456 
457             // reposition the scan at the row just before the next one to 
458             // return.  This routine handles the mess of repositioning if the 
459             // row or the page has disappeared. This can happen if a lock was 
460             // not held on the row while not holding the latch.
461             open_conglom.latchPageAndRepositionScan(pos);
462 
463             // set up for scan to at the specified record handle (position one
464             // before it so that the loop increment and find it).
465             pos.current_slot -= 1;
466         }
467 
468         pos.current_rh    = null;
469         this.stat_numpages_visited  = 1;
470         this.scan_state             = SCAN_INPROGRESS;
471     }
472 
473     /**
474      * Position scan to slot before first slot on next page.
475      * <p>
476    * @exception  StandardException  Standard exception policy.
477      **/
478     protected void positionAtNextPage(
479     RowPosition pos)
480         throws StandardException
481     {
482         // The current_page can become null, in a rare multi-user case, where
483         // all pages in the heap are deallocated, in the middle of the scan
484         // loop, when no latches are held, and the scan is waiting on a lock.
485         // In this case the lockPositionForRead code, has nowhere good to 
486         // position the scan, so it just sets the page to null and returns.
487         if (pos.current_page != null)
488         {
489             // save current page number.
490             long pageid = pos.current_page.getPageNumber();
491 
492             // unlatch old page.
493             pos.unlatch();
494 
495             // latch page after current page number.
496             pos.current_page = 
497                 open_conglom.getContainer().getNextPage(pageid);
498 
499             // set up for scan to continue at beginning of this new page.
500             pos.current_slot = Page.FIRST_SLOT_NUMBER - 1;
501         }
502     }
503 
504     /**
505      * Do any necessary work to complete the scan.
506      *
507    * @exception  StandardException  Standard exception policy.
508      **/
509     protected void positionAtDoneScan(
510     RowPosition pos)
511         throws StandardException
512     {
513         // Unlatch current page if any.
514         pos.unlatch();
515 
516         // unlock the previous row.
517         if (scan_position.current_rh != null)
518         {
519             open_conglom.unlockPositionAfterRead(scan_position);
520             scan_position.current_rh = null;
521         }
522 
523         this.scan_state = SCAN_DONE;
524     }
525 
526   public void reopenScanByRowLocation(
527     RowLocation startRowLocation,
528     Qualifier qualifier[][])
529         throws StandardException
530     {
531         throw StandardException.newException(
532                 SQLState.BTREE_UNIMPLEMENTED_FEATURE);
533     }
534 
535     /**************************************************************************
536      * Protected methods of This class:
537      **************************************************************************
538      */
539 
540     /**
541      * Create object which represents the scan position.
542      * <p>
543      * Designed so that extending classes can override and allocate 
544      * implementation specific row position's.
545      *
546    * @exception  StandardException  Standard exception policy.
547      **/
548     protected RowPosition allocateScanPosition()
549         throws StandardException
550     {
551         return(new RowPosition());
552     }
553 
554     /**
555      * Fetch the next N rows from the table.
556      * <p>
557      * Utility routine used by both fetchSet() and fetchNextGroup().
558      *
559    * @exception  StandardException  Standard exception policy.
560      **/
561     protected int fetchRows(
562     DataValueDescriptor[][] row_array,
563     RowLocation[]           rowloc_array,
564     BackingStoreHashtable   hash_table,
565     long                    max_rowcnt,
566     int[]                   key_column_numbers)
567         throws StandardException
568   {
569         int                     ret_row_count           = 0;
570         DataValueDescriptor[]   fetch_row               = null;
571 
572         if (max_rowcnt == -1)
573             max_rowcnt = Long.MAX_VALUE;
574 
575         if (SanityManager.DEBUG)
576         {
577             if (row_array != null)
578             {
579                 SanityManager.ASSERT(row_array[0] != null,
580                     "first array slot in fetchNextGroup() must be non-null.");
581                 SanityManager.ASSERT(hash_table == null);
582             }
583             else
584             {
585                 SanityManager.ASSERT(hash_table != null);
586             }
587         }
588 
589         if (this.scan_state == SCAN_INPROGRESS)
590         {
591             positionAtResumeScan(scan_position);
592         }
593         else if (this.scan_state == SCAN_INIT)
594         {
595             positionAtStartForForwardScan(scan_position);
596 
597         }
598         else if (this.scan_state == SCAN_HOLD_INPROGRESS)
599         {
600             open_conglom.reopen();
601 
602             if (SanityManager.DEBUG)
603             {
604                 SanityManager.ASSERT(
605                     scan_position.current_rh != null, this.toString()); 
606             }
607 
608             // reposition the scan at the row just before the next one to 
609             // return.
610             // This routine handles the mess of repositioning if the row or 
611             // the page has disappeared. This can happen if a lock was not 
612             // held on the row while not holding the latch.
613             open_conglom.latchPageAndRepositionScan(scan_position);
614 
615             this.scan_state = SCAN_INPROGRESS;
616         }
617         else if (this.scan_state == SCAN_HOLD_INIT)
618         {
619             open_conglom.reopen();
620 
621             positionAtStartForForwardScan(scan_position);
622 
623         }
624         else
625         {
626             if (SanityManager.DEBUG)
627                 SanityManager.ASSERT(this.scan_state == SCAN_DONE);
628 
629             return(0);
630         }
631 
632         // At this point:
633         // scan_position.current_page is latched.  
634         // scan_position.current_slot is the slot on scan_position.current_page
635         // just before the "next" record this routine should process.
636 
637         // loop through successive pages and successive slots on those
638         // pages.  Stop when either the last page is reached 
639         // (scan_position.current_page will be null).  
640         // Along the way apply qualifiers to skip rows which don't qualify.
641 
642     while (scan_position.current_page != null)
643     {
644       while ((scan_position.current_slot + 1) < 
645                     scan_position.current_page.recordCount())
646       {
647                 // unlock the previous row.
648                 if (scan_position.current_rh != null)
649                 {
650                     open_conglom.unlockPositionAfterRead(scan_position);
651 
652                 }
653                 // Allocate a new row to read the row into.
654                 if (fetch_row == null)
655                 {
656                     if (hash_table == null)
657                     {
658                          // point at allocated row in array if one exists.
659                         if (row_array[ret_row_count] == null)
660                         {
661                             row_array[ret_row_count] = 
662                               open_conglom.getRuntimeMem().get_row_for_export();
663                         }
664 
665                         fetch_row = row_array[ret_row_count];
666                     }
667                     else
668                     {
669                         fetch_row = 
670                             open_conglom.getRuntimeMem().get_row_for_export();
671                     }
672                 }
673 
674                 // move scan current position forward.
675                 scan_position.positionAtNextSlot();
676 
677                 // Lock the row.
678                 boolean lock_granted_while_latch_held = 
679                     open_conglom.lockPositionForRead(
680                         scan_position, (RowPosition) null, true, true);
681 
682                 if (!lock_granted_while_latch_held)
683                 {
684                     // if lock could not be granted while holding
685                     // latch, then the row may either be on the same page 
686                     // or it may no longer exist, this implementation does not
687                     // handle rows which move to different pages.  
688                     // 
689                     // If the row moved on the same page then 
690                     // lockPositionForRead() will have automatically updated
691                     // the scan_postion argument to point to it, and we 
692                     // wil now have a latch and a lock on that row.
693                     //
694                     // If the row no longer exists then the 
695                     // "moveForwardIfRowDisappears" argument makes this routine
696                     // find the "next" row in the heap and position on it.  If
697                     // a valid row exists in the current page to position on,
698                     // then lockPositionForRead() will position on it, get
699                     // a lock on it, and return with a latch on the page.  
700                     // Otherwise the routine will return with current_slot == -1
701                     // and it is up to this routine to continue the scan as
702                     // normal at the top of the loop.
703 
704                     if (scan_position.current_page == null)
705                     {
706                         // page has been unlatched and the scan is done, there
707                         // are no more pages.  getNextPage() has been coded to
708                         // handle a null current_page.
709 
710                         break;
711                     }
712                     else if (scan_position.current_slot == -1)
713                     {
714                         // This means that lockPositionForRead() had to 
715                         // reposition the scan forward to a new page, because 
716                         // the row the scan was locking was purged, when the 
717                         // latch was released to wait on the lock.  In this 
718                         // case just jump back to the top of loop and continue 
719                         // scan.
720 
721                         if (SanityManager.DEBUG)
722                         {
723                             SanityManager.ASSERT(
724                                 scan_position.current_rh == null);
725                         }
726 
727                         continue;
728                     }
729                 }
730 
731                 this.stat_numrows_visited++;
732 
733                 // lockRowAtPosition set pos.current_rh as part of getting lock.
734                 if (SanityManager.DEBUG)
735                 {
736                     SanityManager.ASSERT(scan_position.current_rh != null);
737 
738                     // make sure current_rh and current_slot are in sync
739                     if (scan_position.current_slot !=
740                             scan_position.current_page.getSlotNumber(
741                                 scan_position.current_rh))
742                     {
743                         SanityManager.THROWASSERT(
744                             "current_slot = " + scan_position.current_slot +
745                             "current_rh = " + scan_position.current_rh +
746                             "current_rh.slot = " + 
747                             scan_position.current_page.getSlotNumber(
748                                 scan_position.current_rh));
749                     }
750                 }
751 
752                 // fetchFromSlot returns null if row does not qualify.
753 
754                 scan_position.current_rh_qualified =
755                     (scan_position.current_page.fetchFromSlot(
756                         scan_position.current_rh, 
757                         scan_position.current_slot, 
758                         fetch_row, 
759                         init_fetchDesc,
760                         false) != null);
761 
762                 if (scan_position.current_rh_qualified)
763                 {
764                     // qualifying row.  
765 
766 
767                     // scan_position.current_rh is save position of scan while 
768                     // latch is not held.  It currently points at the 
769                     // scan_position.current_slot in search (while latch is 
770                     // held).
771                     if (SanityManager.DEBUG)
772                     {
773                         // make sure current_rh and current_slot are in sync
774                         SanityManager.ASSERT(
775                             scan_position.current_slot ==
776                                 scan_position.current_page.getSlotNumber(
777                                     scan_position.current_rh));
778                     }
779 
780                     // Found qualifying row.  Done fetching rows for the group?
781                     ret_row_count++;
782                     stat_numrows_qualified++;
783 
784 
785                     if (hash_table == null)
786                     {
787                         if (rowloc_array != null)
788                         {
789                             // if requested return the associated row location.
790                             setRowLocationArray(
791                                 rowloc_array, ret_row_count - 1, scan_position);
792                         }
793 
794                         fetch_row = null;
795                     }
796                     else
797                     {
798                         if (hash_table.put(false, fetch_row))
799                         {
800                             // The row was inserted into the hash table so we
801                             // need to create a new row next time through.
802                             fetch_row = null;
803                         }
804                     }
805 
806                     if (max_rowcnt <= ret_row_count) 
807                     {
808                         // exit fetch row loop and return to the client.
809                         scan_position.unlatch();
810 
811                         if (SanityManager.DEBUG)
812                         {
813                             SanityManager.ASSERT(
814                                 scan_position.current_rh != null);
815                         }
816 
817                         return(ret_row_count);
818                     }
819                 }
820       }
821 
822             positionAtNextPage(scan_position);
823 
824             this.stat_numpages_visited++;
825     }
826 
827         // Reached last page of scan.
828         positionAtDoneScan(scan_position);
829 
830         // we need to decrement when we stop scan at the end of the table.
831         this.stat_numpages_visited--;
832 
833     return(ret_row_count);
834     }
835 
836     /**
837     Reposition the current scan.  This call is semantically the same as if
838     the current scan had been closed and a openScan() had been called instead.
839     The scan is reopened against the same conglomerate, and the scan
840     is reopened with the same "scan column list", "hold" and "forUpdate"
841     parameters passed in the original openScan.  
842     <p>
843     The statistics gathered by the scan are not reset to 0 by a reopenScan(),
844     rather they continue to accumulate.
845     <p>
846     Note that this operation is currently only supported on Heap conglomerates.
847     Also note that order of rows within are heap are not guaranteed, so for
848     instance positioning at a RowLocation in the "middle" of a heap, then
849     inserting more data, then continuing the scan is not guaranteed to see
850     the new rows - they may be put in the "beginning" of the heap.
851 
852   @param startRecordHandle  An existing RecordHandle within the conglomerate,
853     at which to position the start of the scan.  The scan will begin at this
854     location and continue forward until the end of the conglomerate.  
855     Positioning at a non-existent RowLocation (ie. an invalid one or one that
856     had been deleted), will result in an exception being thrown when the 
857     first next operation is attempted.
858 
859   @param qualifier An array of qualifiers which, applied
860   to each key, restrict the rows returned by the scan.  Rows
861   for which any one of the qualifiers returns false are not
862   returned by the scan. If null, all rows are returned.
863 
864   @exception StandardException Standard exception policy.
865     **/
866     protected void reopenScanByRecordHandle(
867     RecordHandle    startRecordHandle,
868     Qualifier       qualifier[][])
869         throws StandardException
870     {
871         // initialize scan position parameters at beginning of scan
872         this.scan_state = 
873             (!open_conglom.getHold() ? SCAN_INIT : SCAN_HOLD_INIT);
874 
875         // position the scan at the row before the given record id, so that
876         // the first "next" starts on the given row.
877         scan_position.current_rh = startRecordHandle;
878     }
879 
880     protected void setRowLocationArray(
881     RowLocation[]   rowloc_array,
882     int             index,
883     RowPosition     pos)
884         throws StandardException
885     {
886         throw(StandardException.newException(
887                 SQLState.HEAP_UNIMPLEMENTED_FEATURE));
888     }
889 
890     /**************************************************************************
891      * abstract protected Methods of This class:
892      **************************************************************************
893      */
894 
895     /**************************************************************************
896      * Public Methods of This class:
897      **************************************************************************
898      */
899   public void init(
900     OpenConglomerate                open_conglom,
901   FormatableBitSet                    scanColumnList,
902     DataValueDescriptor[]          startKeyValue,
903     int                             startSearchOperator,
904     Qualifier                       qualifier[][],
905     DataValueDescriptor[]          stopKeyValue,
906     int                             stopSearchOperator)
907         throws StandardException
908     {
909         super.init(open_conglom);
910 
911         // RESOLVE (mikem) - move this into runtime_mem
912         scan_position = allocateScanPosition();
913 
914         // remember inputs
915         init_scanColumnList         = scanColumnList;
916 
917         positionAtInitScan(
918             startKeyValue,
919             startSearchOperator,
920             qualifier,
921             stopKeyValue,
922             stopSearchOperator,
923             scan_position);
924     }
925 
926 
927     public final int getNumPagesVisited()
928     {
929         return(stat_numpages_visited);
930     }
931     public final int getNumRowsVisited()
932     {
933         return(stat_numrows_visited);
934     }
935     public final int getNumRowsQualified()
936     {
937         return(stat_numrows_qualified);
938     }
939     public final FormatableBitSet getScanColumnList()
940     {
941         return(init_scanColumnList);
942     }
943     public final DataValueDescriptor[] getStartKeyValue()
944     {
945         return(init_startKeyValue);
946     }
947     public final int getStartSearchOperator()
948     {
949         return(init_startSearchOperator);
950     }
951     public final DataValueDescriptor[] getStopKeyValue()
952     {
953         return(init_stopKeyValue);
954     }
955     public final int getStopSearchOperator()
956     {
957         return(init_stopSearchOperator);
958     }
959     public final Qualifier[][] getQualifier()
960     {
961         return(init_qualifier);
962     }
963 
964 
965     public final int getScanState()
966     {
967         return(scan_state);
968     }
969     public final void setScanState(int state)
970     {
971         scan_state = state;
972     }
973     public final RowPosition getScanPosition()
974     {
975         return(scan_position);
976     }
977     public final void setScanPosition(RowPosition   pos)
978     {
979         scan_position = pos;
980     }
981 
982     /**************************************************************************
983      * Public Methods implementing ScanController:
984      **************************************************************************
985      */
986     private void closeScan()
987         throws StandardException
988     {
989         super.close();
990 
991     // If we are closed due to catching an error in the middle of init,
992     // xact_manager may not be set yet. 
993     if (open_conglom.getXactMgr() != null)
994       open_conglom.getXactMgr().closeMe(this);
995 
996         // help the garbage collector.
997         this.init_qualifier         = null;
998         init_scanColumnList         = null;
999         init_startKeyValue          = null;
1000        init_stopKeyValue           = null;
1001    }
1002
1003    public void close()
1004        throws StandardException
1005  {
1006        // Finish the scan - this may release locks if read committed and scan
1007        // still holds some locks, and close comes before scan.next() returned
1008        // that scan was done.
1009        positionAtDoneScan(scan_position);
1010
1011        closeScan();
1012  }
1013
1014    public boolean closeForEndTransaction(
1015    boolean closeHeldScan)
1016        throws StandardException
1017  {
1018        if ((!open_conglom.getHold()) || closeHeldScan) 
1019        {
1020            // close the scan as part of the commit/abort
1021
1022            this.scan_state = SCAN_DONE;
1023
1024            closeScan();
1025
1026            return(true);
1027        }
1028        else
1029        {
1030            super.close();
1031
1032            // allow the scan to continue after the commit.
1033            // locks and latches will be released as part of the commit, so
1034            // no need to release them by hand.
1035
1036            if (this.scan_state == SCAN_INPROGRESS)
1037                this.scan_state = SCAN_HOLD_INPROGRESS;
1038            else if (this.scan_state == SCAN_INIT)
1039                this.scan_state = SCAN_HOLD_INIT;
1040
1041
1042            return(false);
1043        }
1044  }
1045
1046
1047    /**
1048  @see ScanController#delete
1049  **/
1050    public boolean delete()
1051    throws StandardException
1052  {
1053        repositionScanForUpateOper();
1054
1055        boolean ret_val = true;
1056
1057        // RESOLVE (mikem) - RECID - performance could be better if we did not
1058        // have to call isDeletedAtSlot().
1059
1060        // RESOLVE (mikem) - share code below with conglomerateController.
1061
1062        if (scan_position.current_page.isDeletedAtSlot(
1063                scan_position.current_slot))
1064        {
1065            ret_val = false;
1066        }
1067        else
1068        {
1069            // Delete the row 
1070            scan_position.current_page.deleteAtSlot(
1071                scan_position.current_slot, true, (LogicalUndo) null);
1072
1073            if (scan_position.current_page.nonDeletedRecordCount() == 0)
1074            {
1075                queueDeletePostCommitWork(scan_position);
1076            }
1077        }
1078
1079        scan_position.unlatch();
1080
1081        return(ret_val);
1082  }
1083
1084
1085    /**
1086     * A call to allow client to indicate that current row does not qualify.
1087     * <p>
1088     * Indicates to the ScanController that the current row does not
1089     * qualify for the scan.  If the isolation level of the scan allows, 
1090     * this may result in the scan releasing the lock on this row.
1091     * <p>
1092     * Note that some scan implimentations may not support releasing locks on 
1093     * non-qualifying rows, or may delay releasing the lock until sometime
1094     * later in the scan (ie. it may be necessary to keep the lock until 
1095     * either the scan is repositioned on the next row or page).
1096     * <p>
1097     * This call should only be made while the scan is positioned on a current
1098     * valid row.
1099     * RESOLVE (mikem-05/29/98) - Implement this when we support levels of
1100     * concurrency less than serializable.
1101     *
1102   * @exception  StandardException  Standard exception policy.
1103     **/
1104    public void didNotQualify()
1105        throws StandardException
1106    {
1107    }
1108
1109    /**
1110     * Insert all rows that qualify for the current scan into the input
1111     * Hash table.  
1112     * <p>
1113     * This routine scans executes the entire scan as described in the 
1114     * openScan call.  For every qualifying unique row value an entry is
1115     * placed into the HashTable. For unique row values the entry in the
1116     * Hashtable has a key value of the object stored in 
1117     * row[key_column_number], and the value of the data is row.  For row 
1118     * values with duplicates, the key value is also row[key_column_number], 
1119     * but the value of the data is a Vector of
1120     * rows.  The caller will have to call "instanceof" on the data value
1121     * object if duplicates are expected, to determine if the data value
1122     * of the Hashtable entry is a row or is a Vector of rows.
1123     * <p>
1124     * Note, that for this routine to work efficiently the caller must 
1125     * ensure that the object in row[key_column_number] implements 
1126     * the hashCode and equals method as appropriate for it's datatype.
1127     * <p>
1128     * It is expected that this call will be the first and only call made in
1129     * an openscan.  Qualifiers and stop position of the openscan are applied
1130     * just as in a normal scan.  This call is logically equivalent to the 
1131     * caller performing the following:
1132     *
1133     * import java.util.Hashtable;
1134     *
1135     * hash_table = new Hashtable();
1136     *
1137     * while (next())
1138     * {
1139     *     row = create_new_row();
1140     *     fetch(row);
1141     *     if ((duplicate_value = 
1142     *         hash_table.put(row[key_column_number], row)) != null)
1143     *     {
1144     *         Vector row_vec;
1145     *
1146     *         // inserted a duplicate
1147     *         if ((duplicate_value instanceof vector))
1148     *         {
1149     *             row_vec = (Vector) duplicate_value;
1150     *         }
1151     *         else
1152     *         {
1153     *             // allocate vector to hold duplicates
1154     *             row_vec = new Vector(2);
1155     *
1156     *             // insert original row into vector
1157     *             row_vec.addElement(duplicate_value);
1158     *
1159     *             // put the vector as the data rather than the row
1160     *             hash_table.put(row[key_column_number], row_vec);
1161     *         }
1162     *         
1163     *         // insert new row into vector
1164     *         row_vec.addElement(row);
1165     *     }
1166     * }
1167     * <p>
1168     * The columns of the row will be the standard columns returned as
1169     * part of a scan, as described by the validColumns - see openScan for
1170     * description.
1171     * RESOLVE - is this ok?  or should I hard code somehow the row to
1172     *           be the first column and the row location?
1173     * <p>
1174     * Currently it is only possible to hash on the first column in the
1175     * conglomerate, in the future we may change the interface to allow
1176     * hashing either on a different column or maybe on a combination of
1177     * columns.
1178     * <p>
1179     * No overflow to external storage is provided, so calling this routine
1180     * on a 1 gigabyte conglomerate will incur at least 1 gigabyte of memory
1181     * (probably failing with a java out of memory condition).  If this
1182     * routine gets an out of memory condition, or if "max_rowcnt" is 
1183     * exceeded then then the routine will give up, empty the Hashtable, 
1184     * and return "false."
1185     * <p>
1186     * On exit from this routine, whether the fetchSet() succeeded or not
1187     * the scan is complete, it is positioned just the same as if the scan
1188     * had been drained by calling "next()" until it returns false (ie. 
1189     * fetchNext() and next() calls will return false).  
1190     * reopenScan() can be called to restart the scan.
1191     * <p>
1192     *
1193     * RESOLVE - until we get row counts what should we do for sizing the
1194     *           the size, capasity, and load factor of the hash table.
1195     *           For now it is up to the caller to create the Hashtable,
1196     *           Access does not reset any parameters.
1197     * <p>
1198     * RESOLVE - I am not sure if access should be in charge of allocating
1199     *           the new row objects.  I know that I can do this in the
1200     *           case of btree's, but I don't think I can do this in heaps.
1201     *           Maybe this is solved by work to be done on the sort 
1202     *           interface.
1203     *
1204     *
1205   * @return boolean indicating that the fetch set succeeded.  If it failed
1206     *                 Hashtable.clear() will be called leaving an empty 
1207     *                 table.
1208     *
1209     * @param max_rowcnt        The maximum number of rows to insert into the 
1210     *                          Hash table.  Pass in -1 if there is no maximum.
1211     * @param key_column_numbers The column numbers of the columns in the
1212     *                          scan result row to be the key to the Hashtable.
1213     *                          "0" is the first column in the scan result
1214     *                          row (which may be different than the first
1215     *                          column in the row in the table of the scan).
1216     * @param hash_table        The java HashTable to load into.
1217     *
1218   * @exception  StandardException  Standard exception policy.
1219     **/
1220    public void fetchSet(
1221    long                    max_rowcnt,
1222    int[]                   key_column_numbers,
1223    BackingStoreHashtable   hash_table)
1224        throws StandardException
1225  {
1226        fetchRows(
1227            (DataValueDescriptor[][]) null,
1228            (RowLocation[]) null,
1229            hash_table,
1230            max_rowcnt,
1231            key_column_numbers);
1232
1233        return;
1234    }
1235
1236    /**
1237    Reposition the current scan.  This call is semantically the same as if
1238    the current scan had been closed and a openScan() had been called instead.
1239    The scan is reopened with against the same conglomerate, and the scan
1240    is reopened with the same "hold" and "forUpdate" parameters passed in
1241    the original openScan.  The previous template row continues to be used.
1242
1243  @param startKeyValue  An indexable row which holds a 
1244  (partial) key value which, in combination with the
1245  startSearchOperator, defines the starting position of
1246  the scan.  If null, the starting position of the scan
1247  is the first row of the conglomerate.
1248  
1249  @param startSearchOperator an operator which defines
1250  how the startKeyValue is to be searched for.  If 
1251    startSearchOperator is ScanController.GE, the scan starts on
1252  the first row which is greater than or equal to the 
1253  startKeyValue.  If startSearchOperation is ScanController.GT,
1254  the scan starts on the first row whose key is greater than
1255  startKeyValue.  The startSearchOperation parameter is 
1256  ignored if the startKeyValue parameter is null.
1257
1258  @param qualifier An array of qualifiers which, applied
1259  to each key, restrict the rows returned by the scan.  Rows
1260  for which any one of the qualifiers returns false are not
1261  returned by the scan. If null, all rows are returned.
1262
1263  @param stopKeyValue  An indexable row which holds a 
1264  (partial) key value which, in combination with the
1265  stopSearchOperator, defines the ending position of
1266  the scan.  If null, the ending position of the scan
1267  is the last row of the conglomerate.
1268  
1269  @param stopSearchOperator an operator which defines
1270  how the stopKeyValue is used to determine the scan stopping
1271  position. If stopSearchOperation is ScanController.GE, the scan 
1272  stops just before the first row which is greater than or
1273  equal to the stopKeyValue.  If stopSearchOperation is
1274  ScanController.GT, the scan stops just before the first row whose
1275  key is greater than  startKeyValue.  The stopSearchOperation
1276  parameter is ignored if the stopKeyValue parameter is null.
1277
1278  @exception StandardException Standard exception policy.
1279    **/
1280  public void reopenScan(
1281    DataValueDescriptor[]   startKeyValue,
1282    int                     startSearchOperator,
1283    Qualifier               qualifier[][],
1284    DataValueDescriptor[]   stopKeyValue,
1285    int                     stopSearchOperator)
1286        throws StandardException
1287    {
1288        if (SanityManager.DEBUG)
1289        {
1290            if (!open_conglom.getHold())
1291            {
1292                SanityManager.ASSERT(
1293                    !open_conglom.isClosed(), 
1294                    "GenericScanController.reopenScan() called on a non-held closed scan.");
1295            }
1296        }
1297
1298        // initialize scan position parameters at beginning of scan
1299        this.scan_state = 
1300            (!open_conglom.getHold() ? SCAN_INIT : SCAN_HOLD_INIT);
1301
1302        scan_position.current_rh   = null;
1303    }
1304
1305    /**
1306  @see ScanController#replace
1307  **/
1308    public boolean replace(
1309    DataValueDescriptor[]   row, 
1310    FormatableBitSet                 validColumns)
1311    throws StandardException
1312  {
1313        repositionScanForUpateOper();
1314
1315        boolean ret_val = 
1316            scan_position.current_page.update(
1317                scan_position.current_rh, row, validColumns);
1318
1319        scan_position.unlatch();
1320
1321        return(ret_val);
1322  }
1323
1324    /**
1325    Returns true if the current position of the scan still qualifies
1326    under the set of qualifiers passed to the openScan().  When called
1327    this routine will reapply all qualifiers against the row currently
1328    positioned and return true if the row still qualifies.  If the row
1329    has been deleted or no longer passes the qualifiers then this routine
1330    will return false.
1331    
1332    This case can come about if the current scan
1333    or another scan on the same table in the same transaction 
1334    deleted the row or changed columns referenced by the qualifier after 
1335    the next() call which positioned the scan at this row.  
1336
1337    Note that for comglomerates which don't support update, like btree's, 
1338    there is no need to recheck the qualifiers.
1339
1340    The results of a fetch() performed on a scan positioned on 
1341    a deleted row are undefined.
1342
1343  @exception StandardException Standard exception policy.
1344    **/
1345    public boolean doesCurrentPositionQualify()
1346    throws StandardException
1347    {
1348        if (scan_state != SCAN_INPROGRESS)
1349            throw StandardException.newException(
1350                    SQLState.AM_SCAN_NOT_POSITIONED);
1351
1352        if (!open_conglom.latchPage(scan_position))
1353        {
1354            return(false);
1355        }
1356
1357        DataValueDescriptor row[] = 
1358            open_conglom.getRuntimeMem().get_scratch_row();
1359
1360        // If fetchFromSlot returns null it either means the row is deleted,
1361        // or the qualifier evaluates to false.
1362        
1363        boolean ret_val = 
1364            (scan_position.current_page.fetchFromSlot(
1365                scan_position.current_rh, 
1366                scan_position.current_slot, 
1367                row,
1368                init_fetchDesc,
1369                false) != null);
1370
1371        scan_position.unlatch();
1372
1373        return(ret_val);
1374    }
1375
1376    /**
1377  @see ScanController#fetch
1378  **/
1379  public void fetch(DataValueDescriptor[] row)
1380    throws StandardException
1381  {
1382        if (scan_state != SCAN_INPROGRESS)
1383            throw StandardException.newException(
1384                    SQLState.AM_SCAN_NOT_POSITIONED);
1385
1386        if (!open_conglom.latchPage(scan_position))
1387        {
1388            throw StandardException.newException(
1389                    SQLState.AM_RECORD_NOT_FOUND, 
1390                    open_conglom.getContainer().getId(),
1391                    new Long(scan_position.current_rh.getId()));
1392        }
1393
1394        // RESOLVE (mikem) - should this call apply the qualifiers again?
1395        RecordHandle rh = 
1396            scan_position.current_page.fetchFromSlot(
1397                scan_position.current_rh, 
1398                scan_position.current_slot, 
1399                row, 
1400                init_fetchDesc, 
1401                false);
1402
1403        scan_position.unlatch();
1404
1405        if (rh == null)
1406        {
1407            /*
1408            if (SanityManager.DEBUG)
1409            {
1410                if (isCurrentPositionDeleted())
1411                    SanityManager.THROWASSERT(
1412                        "The record (" + 
1413                        open_conglom.getContainer().getId() +
1414                        ", " +
1415                        scan_position.current_rh.getPageNumber() + ", " +
1416                        scan_position.current_rh.getId() + ") " +
1417                        "being fetched is marked deleted on page.:\n");
1418            }
1419            */
1420
1421            throw StandardException.newException(
1422                    SQLState.AM_RECORD_NOT_FOUND, 
1423                    open_conglom.getContainer().getId(),
1424                    new Long(scan_position.current_rh.getId()));
1425        }
1426
1427        return;
1428  }
1429
1430  /**
1431  Fetch the location of the current position in the scan.
1432  @see ScanController#fetchLocation
1433
1434  @exception  StandardException  Standard exception policy.
1435  **/
1436  public void fetchLocation(RowLocation templateLocation)
1437    throws StandardException
1438  {
1439        throw StandardException.newException(
1440                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1441  }
1442
1443    /**
1444     * Return ScanInfo object which describes performance of scan.
1445     * <p>
1446     * Return ScanInfo object which contains information about the current
1447     * scan.
1448     * <p>
1449     *
1450     * @see ScanInfo
1451     *
1452   * @return The ScanInfo object which contains info about current scan.
1453     *
1454   * @exception  StandardException  Standard exception policy.
1455     **/
1456    public ScanInfo getScanInfo()
1457    throws StandardException
1458    {
1459        throw StandardException.newException(
1460                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1461    }
1462
1463
1464
1465    /**
1466    Returns true if the current position of the scan is at a 
1467    deleted row.  This case can come about if the current scan
1468    or another scan on the same table in the same transaction 
1469    deleted the row after the next() call which positioned the
1470    scan at this row.  
1471
1472    The results of a fetch() performed on a scan positioned on 
1473    a deleted row are undefined.
1474
1475  @exception StandardException Standard exception policy.
1476    **/
1477    public boolean isCurrentPositionDeleted()
1478    throws StandardException
1479    {
1480        if (scan_state != SCAN_INPROGRESS)
1481            throw StandardException.newException(
1482                    SQLState.AM_SCAN_NOT_POSITIONED);
1483
1484        if (!open_conglom.latchPage(scan_position))
1485        {
1486            return(true);
1487        }
1488
1489        boolean ret_val = 
1490            scan_position.current_page.isDeletedAtSlot(
1491                scan_position.current_slot);
1492
1493        scan_position.unlatch();
1494
1495        return(ret_val);
1496    }
1497}