KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > store > access > btree > BTreeScan


1 /*
2
3    Derby - Class org.apache.derby.impl.store.access.btree.BTreeScan
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.btree;
23
24 import org.apache.derby.iapi.reference.SQLState;
25
26 import org.apache.derby.iapi.services.sanity.SanityManager;
27 import org.apache.derby.iapi.services.io.Storable;
28
29 import org.apache.derby.iapi.error.StandardException;
30
31 import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;
32 import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
33 import org.apache.derby.iapi.store.access.conglomerate.ScanManager;
34 import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
35
36 import org.apache.derby.iapi.store.access.ConglomerateController;
37 import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
38 import org.apache.derby.iapi.store.access.GenericScanController;
39 import org.apache.derby.iapi.store.access.Qualifier;
40 import org.apache.derby.iapi.store.access.RowUtil;
41 import org.apache.derby.iapi.store.access.ScanController;
42 import org.apache.derby.iapi.store.access.ScanInfo;
43 import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
44 import org.apache.derby.iapi.store.access.TransactionController;
45
46 import org.apache.derby.iapi.store.raw.ContainerHandle;
47 import org.apache.derby.iapi.store.raw.FetchDescriptor;
48 import org.apache.derby.iapi.store.raw.LockingPolicy;
49 import org.apache.derby.iapi.store.raw.Page;
50 import org.apache.derby.iapi.store.raw.RecordHandle;
51 import org.apache.derby.iapi.store.raw.Transaction;
52
53 import org.apache.derby.iapi.types.DataValueDescriptor;
54
55 import org.apache.derby.iapi.types.RowLocation;
56
57 import org.apache.derby.impl.store.access.conglomerate.ConglomerateUtil;
58 import org.apache.derby.impl.store.access.conglomerate.TemplateRow;
59
60 import org.apache.derby.iapi.services.io.FormatableBitSet;
61 import org.apache.derby.iapi.store.access.BackingStoreHashtable;
62
63 /**
64
65   A b-tree scan controller corresponds to an instance of an open b-tree scan.
66   <P>
67   <B>Concurrency Notes<\B>
68   <P>
69   The concurrency rules are derived from OpenBTree.
70   <P>
71   @see OpenBTree
72
73 **/

74
75 public abstract class BTreeScan extends OpenBTree implements ScanManager
76 {
77
78     /*
79     ** Fields of BTreeScan
80     */

81
82     /**
83      * init_startKeyValue, init_qualifier, and init_stopKeyValue all are used
84      * to store * references to the values passed in when ScanController.init()
85      * is called. It is assumed that these are not altered by the client
86      * while the scan is active.
87      */

88     protected Transaction init_rawtran = null;
89     protected boolean init_forUpdate;
90     protected FormatableBitSet init_scanColumnList;
91     protected DataValueDescriptor[] init_template;
92     protected DataValueDescriptor[] init_startKeyValue;
93     protected int init_startSearchOperator = 0;
94     protected Qualifier init_qualifier[][] = null;
95     protected DataValueDescriptor[] init_stopKeyValue;
96     protected int init_stopSearchOperator = 0;
97     protected boolean init_hold;
98
99
100     /**
101      * The fetch descriptor which describes the row to be returned by the scan.
102      **/

103     protected FetchDescriptor init_fetchDesc;
104
105
106     /**
107      * A constant FetchDescriptor which describes the position of the
108      * RowLocation field within the btree, currently always the last column).
109      * Used by lock/unlock to fetch the RowLocation.
110      * Only needs to be allocated once per scan.
111      **/

112     protected FetchDescriptor init_lock_fetch_desc;
113
114
115     BTreeRowPosition scan_position;
116
117
118      /**
119       * Whether the scan should requests UPDATE locks which then will be
120       * converted to X locks when the actual operation is performed.
121      **/

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

165     protected static final int SCAN_INIT = 1;
166     protected static final int SCAN_INPROGRESS = 2;
167     protected static final int SCAN_DONE = 3;
168     protected static final int SCAN_HOLD_INIT = 4;
169     protected static final int SCAN_HOLD_INPROGRESS = 5;
170
171     /**
172      * Delay positioning the table at the start position until the first
173      * next() call. The initial position is done in positionAtStartPosition().
174      */

175     protected int scan_state = SCAN_INIT;
176
177     /**
178      * Performance counters ...
179      */

180     protected int stat_numpages_visited = 0;
181     protected int stat_numrows_visited = 0;
182     protected int stat_numrows_qualified = 0;
183     protected int stat_numdeleted_rows_visited = 0;
184
185     /**
186      * What kind of row locks to get during the scan.
187      **/

188     protected int lock_operation;
189
190
191     /**
192      * A 1 element array to turn fetchNext and fetch calls into
193      * fetchNextGroup calls.
194      **/

195     protected DataValueDescriptor[][] fetchNext_one_slot_array =
196                                             new DataValueDescriptor[1][];
197
198     /* Constructors for This class: */
199
200     public BTreeScan()
201     {
202     }
203
204     /*
205     ** Private/Protected methods of This class, sorted alphabetically
206     */

207
208     /**
209      * Fetch the next N rows from the table.
210      * <p>
211      * Utility routine used by both fetchSet() and fetchNextGroup().
212      *
213      * @exception StandardException Standard exception policy.
214      **/

215     abstract protected int fetchRows(
216     BTreeRowPosition pos,
217     DataValueDescriptor[][] row_array,
218     RowLocation[] rowloc_array,
219     BackingStoreHashtable hash_table,
220     long max_rowcnt,
221     int[] key_column_numbers)
222         throws StandardException;
223
224
225     /**
226      * Shared initialization code between init() and reopenScan().
227      * <p>
228      * Basically save away input parameters describing qualifications for
229      * the scan, and do some error checking.
230      *
231      * @exception StandardException Standard exception policy.
232      **/

233     private void initScanParams(
234     DataValueDescriptor[] startKeyValue,
235     int startSearchOperator,
236     Qualifier qualifier[][],
237     DataValueDescriptor[] stopKeyValue,
238     int stopSearchOperator)
239         throws StandardException
240     {
241         // startKeyValue init.
242
this.init_startKeyValue = startKeyValue;
243         if (RowUtil.isRowEmpty(this.init_startKeyValue))
244             this.init_startKeyValue = null;
245
246         // startSearchOperator init.
247
this.init_startSearchOperator = startSearchOperator;
248
249         // qualifier init.
250
if ((qualifier != null) && (qualifier .length == 0))
251             qualifier = null;
252         this.init_qualifier = qualifier;
253
254         // stopKeyValue init.
255
this.init_stopKeyValue = stopKeyValue;
256         if (RowUtil.isRowEmpty(this.init_stopKeyValue))
257             this.init_stopKeyValue = null;
258
259         // stopSearchOperator init.
260
this.init_stopSearchOperator = stopSearchOperator;
261
262         // reset the "current" position to starting condition.
263
// RESOLVE (mmm) - "compile" this.
264
scan_position = new BTreeRowPosition();
265
266         scan_position.init();
267
268         scan_position.current_lock_template =
269             new DataValueDescriptor[this.init_template.length];
270
271         scan_position.current_lock_template[this.init_template.length - 1] =
272             scan_position.current_lock_row_loc =
273                 (RowLocation) ((RowLocation)
274                      init_template[init_template.length - 1]).cloneObject();
275
276         // Verify that all columns in start key value, stop key value, and
277
// qualifiers are present in the list of columns described by the
278
// scanColumnList.
279
if (SanityManager.DEBUG)
280         {
281             if (init_scanColumnList != null)
282             {
283                 // verify that all columns specified in qualifiers, start
284
// and stop positions are specified in the scanColumnList.
285

286                 FormatableBitSet required_cols;
287
288                 if (qualifier != null)
289                     required_cols = RowUtil.getQualifierBitSet(qualifier);
290                 else
291                     required_cols = new FormatableBitSet(0);
292
293                 // add in start columns
294
if (this.init_startKeyValue != null)
295                 {
296                     required_cols.grow(this.init_startKeyValue.length);
297                     for (int i = 0; i < this.init_startKeyValue.length; i++)
298                         required_cols.set(i);
299                 }
300
301                 if (this.init_stopKeyValue != null)
302                 {
303                     required_cols.grow(this.init_stopKeyValue.length);
304                     for (int i = 0; i < this.init_stopKeyValue.length; i++)
305                         required_cols.set(i);
306                 }
307
308                 FormatableBitSet required_cols_and_scan_list =
309                     (FormatableBitSet) required_cols.clone();
310
311                 required_cols_and_scan_list.and(init_scanColumnList);
312
313                 // FormatableBitSet equals requires the two FormatableBitSets to be of same
314
// length.
315
required_cols.grow(init_scanColumnList.size());
316
317                 if (!required_cols_and_scan_list.equals(required_cols))
318                 {
319                     SanityManager.THROWASSERT(
320                         "Some column specified in a Btree " +
321                         " qualifier/start/stop list is " +
322                         "not represented in the scanColumnList." +
323                         "\n:required_cols_and_scan_list = " +
324                             required_cols_and_scan_list +
325                         "\n;required_cols = " + required_cols +
326                         "\n;init_scanColumnList = " + init_scanColumnList);
327                 }
328             }
329         }
330     }
331
332     /**
333      * Position scan at "start" position for a forward scan.
334      * <p>
335      * Positions the scan to the slot just before the first record to be
336      * returned from the scan. Returns the start page latched, and
337      * sets "current_slot" to the slot number.
338      * <p>
339      *
340      * @exception StandardException Standard exception policy.
341      **/

342     protected void positionAtStartForForwardScan(
343     BTreeRowPosition pos)
344         throws StandardException
345     {
346         boolean exact;
347
348         // This routine should only be called from first next() call //
349
if (SanityManager.DEBUG)
350         {
351             SanityManager.ASSERT(
352                 (scan_state == SCAN_INIT) || (scan_state == SCAN_HOLD_INIT));
353             SanityManager.ASSERT(pos.current_rh == null);
354             SanityManager.ASSERT(pos.current_positionKey == null);
355             SanityManager.ASSERT(pos.current_scan_pageno == 0);
356         }
357
358         // Loop until you can lock the row previous to the first row to be
359
// returned by the scan, while holding the page latched, without
360
// waiting. If you have to wait, drop the latch, wait for the lock -
361
// which makes it likely if you wait for the lock you will loop just
362
// once, find the same lock satisfies the search and since you already
363
// have the lock it will be granted.
364
while (true)
365         {
366             // Find the starting page and row slot, must start at root and
367
// search either for leftmost leaf, or search for specific key.
368
ControlRow root = ControlRow.Get(this, BTree.ROOTPAGEID);
369
370             // include search of tree in page visited stats.
371
stat_numpages_visited += root.getLevel() + 1;
372
373             boolean need_previous_lock = true;
374
375             if (init_startKeyValue == null)
376             {
377                 // No start given, so position at 0 slot of leftmost leaf page
378
pos.current_leaf = (LeafControlRow) root.searchLeft(this);
379
380                 pos.current_slot = ControlRow.CR_SLOT;
381                 exact = false;
382             }
383             else
384             {
385                 // Search for the starting row.
386

387                 if (SanityManager.DEBUG)
388                     SanityManager.ASSERT(
389                         (init_startSearchOperator == ScanController.GE) ||
390                         (init_startSearchOperator == ScanController.GT));
391
392                 SearchParameters sp = new SearchParameters(
393                     init_startKeyValue,
394                     ((init_startSearchOperator == ScanController.GE) ?
395                         SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH :
396                         SearchParameters.POSITION_RIGHT_OF_PARTIAL_KEY_MATCH),
397                     init_template, this, false);
398
399                 pos.current_leaf = (LeafControlRow) root.search(sp);
400
401                 pos.current_slot = sp.resultSlot;
402                 exact = sp.resultExact;
403
404                 // The way that scans are used, the caller calls next()
405
// to position on the first row. If the result of the
406
// search that found the starting page and slot was not
407
// exact, then the page/slot will refer to the row before
408
// the first qualifying row. The first call to next()
409
// will therefore move to the first (potentially) qualifying
410
// row. However, if the search was exact, then we don't
411
// want to move the position on the first call to next.
412
// In that case, by decrementing the slot, the first call
413
// to next will put us back on the starting row.
414

415                 if (exact && init_startSearchOperator == ScanController.GE)
416                 {
417                     pos.current_slot--;
418
419                     // A scan on a unique index, with a start position of
420
// GE, need not get a previous key lock to protect the
421
// range. Since it is unique no other key can go before
422
// the first row returned from the scan.
423
//
424
// RESOLVE - currently btree's only support allowDuplicates
425
// of "false", so no need to do the extra check, current
426
// btree implementation depends on RowLocation field
427
// making every key unique (duplicate indexes are supported
428
// by the nUniqueColumns and nKeyFields).
429
if (getConglomerate().nUniqueColumns < getConglomerate().nKeyFields)
430                     {
431                         // this implies unique index, thus no prev key.
432
need_previous_lock = false;
433                     }
434                 }
435             }
436
437             boolean latch_released = false;
438             if (need_previous_lock)
439             {
440                 latch_released =
441                     !this.getLockingPolicy().lockScanRow(
442                         this, this.getConglomerate(), pos,
443                         true,
444                         init_lock_fetch_desc,
445                         pos.current_lock_template,
446                         pos.current_lock_row_loc,
447                         true, init_forUpdate,
448                         lock_operation);
449             }
450             else
451             {
452                 // Don't need to lock the "previous key" but still need to get
453
// the scan lock to protect the position in the btree.
454

455                 latch_released =
456                     !this.getLockingPolicy().lockScan(
457                         pos.current_leaf, // the page we are positioned on.
458
(ControlRow) null, // no other page to unlatch
459
false, // lock for read.
460
lock_operation); // not used.
461
}
462
463             // special test to see if latch release code works
464
if (SanityManager.DEBUG)
465             {
466                 latch_released =
467                     test_errors(
468                         this,
469                         "BTreeScan_positionAtStartPosition", true,
470                         this.getLockingPolicy(),
471                         pos.current_leaf, latch_released);
472             }
473
474             if (latch_released)
475             {
476                 // lost latch on pos.current_leaf, search the tree again.
477
pos.current_leaf = null;
478                 continue;
479             }
480             else
481             {
482                 // success! got all the locks, while holding the latch.
483
break;
484             }
485         }
486
487         this.scan_state = SCAN_INPROGRESS;
488         pos.current_scan_pageno = pos.current_leaf.page.getPageNumber();
489         pos.current_slot = pos.current_slot;
490
491         if (SanityManager.DEBUG)
492             SanityManager.ASSERT(pos.current_leaf != null);
493     }
494
495     /**
496      * Position scan at "start" position for a backward scan.
497      * <p>
498      * Positions the scan to the slot just after the first record to be
499      * returned from the backward scan. Returns the start page latched, and
500      * sets "current_slot" to the slot number just right of the first slot
501      * to return.
502      * <p>
503      *
504      * @exception StandardException Standard exception policy.
505      **/

506     protected void positionAtStartForBackwardScan(
507     BTreeRowPosition pos)
508         throws StandardException
509     {
510         boolean exact;
511
512         // This routine should only be called from first next() call //
513
if (SanityManager.DEBUG)
514         {
515             SanityManager.ASSERT(
516                 (this.scan_state == SCAN_INIT) ||
517                 (this.scan_state == SCAN_HOLD_INIT));
518
519             SanityManager.ASSERT(pos.current_rh == null);
520             SanityManager.ASSERT(pos.current_positionKey == null);
521             SanityManager.ASSERT(pos.current_scan_pageno == 0);
522         }
523
524         // Loop until you can lock the row previous to the first row to be
525
// returned by the scan, while holding the page latched, without
526
// waiting. If you have to wait, drop the latch, wait for the lock -
527
// which makes it likely if you wait for the lock you will loop just
528
// once, find the same lock satisfies the search and since you already
529
// have the lock it will be granted.
530
while (true)
531         {
532             // Find the starting page and row slot, must start at root and
533
// search either for leftmost leaf, or search for specific key.
534
ControlRow root = ControlRow.Get(this, BTree.ROOTPAGEID);
535
536             // include search of tree in page visited stats.
537
stat_numpages_visited += root.getLevel() + 1;
538
539             if (init_startKeyValue == null)
540             {
541                 // No start given, position at last slot + 1 of rightmost leaf
542
pos.current_leaf = (LeafControlRow) root.searchRight(this);
543
544                 pos.current_slot = pos.current_leaf.page.recordCount();
545                 exact = false;
546             }
547             else
548             {
549                 /*
550                 if (SanityManager.DEBUG)
551                     SanityManager.THROWASSERT(
552                         "Code not ready yet for positioned backward scans.");
553                         */

554
555
556                 if (SanityManager.DEBUG)
557                     SanityManager.ASSERT(
558                         (init_startSearchOperator == ScanController.GE) ||
559                         (init_startSearchOperator == ScanController.GT));
560
561                 // Search for the starting row.
562

563                 SearchParameters sp = new SearchParameters(
564                     init_startKeyValue,
565                     ((init_startSearchOperator == ScanController.GE) ?
566                         SearchParameters.POSITION_RIGHT_OF_PARTIAL_KEY_MATCH :
567                         SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH),
568                     init_template, this, false);
569
570                 pos.current_leaf = (LeafControlRow) root.search(sp);
571
572                 pos.current_slot = sp.resultSlot;
573                 exact = sp.resultExact;
574
575                 // The way that backward scans are used, the caller calls next()
576
// to position on the first row. If the result of the
577
// search that found the starting page and slot was not
578
// exact, then the page/slot will refer to the row before
579
// the first qualifying row. The first call to next()
580
// will therefore move to the first (potentially) qualifying
581
// row. However, if the search was exact, then we don't
582
// want to move the position on the first call to next.
583
// In that case, by decrementing the slot, the first call
584
// to next will put us back on the starting row.
585

586
587                 if (exact)
588                 {
589                     // the search has found exactly the start position key
590
if (init_startSearchOperator == ScanController.GE)
591                     {
592                         // insure backward scan returns this row by moving
593
// slot to one after this row.
594
pos.current_slot++;
595                     }
596                     else
597                     {
598                         // no work necessary leave startslot positioned on the
599
// row, we will skip this record
600
if (SanityManager.DEBUG)
601                             SanityManager.ASSERT(
602                                 init_startSearchOperator == ScanController.GT);
603                     }
604                 }
605                 else
606                 {
607                     // the search positioned one before the start position key,
608
// move it to one "after"
609
pos.current_slot++;
610                 }
611             }
612
613             boolean latch_released =
614                 !this.getLockingPolicy().lockScanRow(
615                     this, this.getConglomerate(), pos,
616                     true,
617                     init_lock_fetch_desc,
618                     pos.current_lock_template,
619                     pos.current_lock_row_loc,
620                     true, init_forUpdate, lock_operation);
621
622             // special test to see if latch release code works
623
if (SanityManager.DEBUG)
624             {
625                 latch_released =
626                     test_errors(
627                         this,
628                         "BTreeScan_positionAtStartPosition", true,
629                         this.getLockingPolicy(), pos.current_leaf, latch_released);
630             }
631
632             if (latch_released)
633             {
634                 // lost latch on pos.current_leaf, search the tree again.
635
pos.current_leaf = null;
636                 continue;
637             }
638             else
639             {
640                 // success! got all the locks, while holding the latch.
641
break;
642             }
643         }
644
645         this.scan_state = SCAN_INPROGRESS;
646         pos.current_scan_pageno = pos.current_leaf.page.getPageNumber();
647
648         if (SanityManager.DEBUG)
649             SanityManager.ASSERT(pos.current_leaf != null);
650
651         // System.out.println("backward scan end start position: " +
652
// " current_slot = " + this.current_slot );
653
}
654
655     /**
656      * Position scan to 0 slot on next page.
657      * <p>
658      * Position to next page, keeping latch on previous page until we have
659      * latch on next page. This routine releases the latch on current_page
660      * once it has successfully gotten both the latch on the next page and
661      * the scan lock on the next page.
662      *
663      * @param pos current row position of the scan.
664      *
665      * @exception StandardException Standard exception policy.
666      **/

667     protected void positionAtNextPage(
668     BTreeRowPosition pos)
669         throws StandardException
670     {
671         // RESOLVE (mikem) - not sure but someday in the future this
672
// assert may not be true, but for now we always have the scan
673
// lock when we call this routine.
674
if (SanityManager.DEBUG)
675             SanityManager.ASSERT(pos.current_scan_pageno != 0);
676
677         while (true)
678         {
679             if ((pos.next_leaf =
680                  (LeafControlRow) pos.current_leaf.getRightSibling(this)) == null)
681             {
682                 break;
683             }
684
685             boolean latch_released =
686                 !this.getLockingPolicy().lockScan(
687                     pos.next_leaf,
688                     (LeafControlRow) null, // no other latch currently
689
false /* not for update */,
690                     ConglomerateController.LOCK_READ); // get read scan lock.
691

692             // TESTING CODE:
693
if (SanityManager.DEBUG)
694             {
695                 latch_released =
696                     test_errors(
697                         this,
698                         "BTreeScan_positionAtNextPage", true,
699                         this.getLockingPolicy(), pos.next_leaf, latch_released);
700             }
701
702             if (!latch_released)
703             {
704                 break;
705             }
706         }
707
708         // Now that we either have both latch and scan lock on next leaf, or
709
// there is no next leaf we can release scan and latch on current page.
710
if (SanityManager.DEBUG)
711         {
712             if (pos.current_scan_pageno != pos.current_leaf.page.getPageNumber())
713                 SanityManager.THROWASSERT(
714                 "pos.current_scan_pageno = " + pos.current_scan_pageno +
715                 "pos.current_leaf = " + pos.current_leaf);
716         }
717
718         // unlock the previous row if doing read.
719
if (pos.current_rh != null)
720         {
721             this.getLockingPolicy().unlockScanRecordAfterRead(
722                 pos, init_forUpdate);
723         }
724
725         this.getLockingPolicy().unlockScan(
726             pos.current_leaf.page.getPageNumber());
727         pos.current_leaf.release();
728         pos.current_leaf = pos.next_leaf;
729
730         pos.current_scan_pageno =
731             (pos.next_leaf == null) ? 0 : pos.next_leaf.page.getPageNumber();
732
733         // set up for scan to continue at beginning of next page.
734
pos.current_slot = Page.FIRST_SLOT_NUMBER;
735         pos.current_rh = null;
736     }
737
738     /**
739     Position scan at "start" position.
740     <p>
741     Positions the scan to the slot just before the first record to be returned
742     from the scan. Returns the start page latched, and sets "current_slot" to
743     the slot number.
744
745     @exception StandardException Standard exception policy.
746     **/

747     abstract void positionAtStartPosition(
748     BTreeRowPosition pos)
749         throws StandardException;
750
751
752     /**
753      * Do any necessary work to complete the scan.
754      *
755      * @param pos current row position of the scan.
756      *
757      * @exception StandardException Standard exception policy.
758      **/

759     protected void positionAtDoneScanFromClose(
760     BTreeRowPosition pos)
761         throws StandardException
762     {
763         // call unlockScanRecordAfterRead() before closing, currently
764
// this is only important for releasing RR locks on non-qualified
765
// rows.
766
//
767
// Otherwise the correct behavior happens as part of the close, ie.:
768
//
769
// for READ_UNCOMMITTED there is no lock to release,
770
// for READ_COMMITTED all read locks will be released,
771
// for REPEATABLE_READ or SERIALIZABLE no locks are released.
772

773         if ((pos.current_rh != null) && !pos.current_rh_qualified)
774         {
775             if (pos.current_leaf == null || pos.current_leaf.page == null)
776             {
777                 // If we are being called from a "normal" close then there
778
// will be no latch on current_leaf, get it and do the the
779
// unlock. We may be called sometimes, after an error where
780
// we may have the latch, in this case the transaction is about
781
// to be backed out anyway so don't worry about doing this
782
// unlock (thus why we only do the following code if we
783
// "don't" have lock, ie. pos.current_leaf== null).
784

785                 if (!reposition(pos, false))
786                 {
787                     if (SanityManager.DEBUG)
788                     {
789                         SanityManager.THROWASSERT(
790                             "can not fail while holding update row lock.");
791                     }
792                 }
793
794                 this.getLockingPolicy().unlockScanRecordAfterRead(
795                     pos, init_forUpdate);
796
797                 pos.current_rh = null;
798                 pos.current_leaf.release();
799                 pos.current_leaf = null;
800             }
801         }
802
803
804         // Need to do this unlock in any case, until lock manager provides
805
// a way to release locks associated with a compatibility space. This
806
// scan lock is special, as it is a lock on the btree container rather
807
// than the heap container. The open container on the btree actually
808
// has a null locking policy so the close of that container does not
809
// release this lock, need to explicitly unlock it here or when the
810
// scan is closed as part of the abort the lock will not be released.
811
if (pos.current_scan_pageno != 0)
812         {
813             this.getLockingPolicy().unlockScan(pos.current_scan_pageno);
814             pos.current_scan_pageno = 0;
815         }
816
817         pos.current_slot = Page.INVALID_SLOT_NUMBER;
818         pos.current_rh = null;
819         pos.current_positionKey = null;
820         this.scan_state = SCAN_DONE;
821
822         return;
823     }
824
825     /**
826      * Do work necessary to close a scan.
827      * <p>
828      * This routine can only be called "inline" from other btree routines,
829      * as it counts on the state of the pos to be correct.
830      * <p>
831      * Closing a scan from close() must handle long jumps from exceptions
832      * where the state of pos may not be correct. The easiest case is
833      * a lock timeout which has caused us not to have a latch on a page,
834      * but pos still thinks there is a latch. This is the easiest but
835      * other exceptions can also caused the same state at close() time.
836      **/

837     protected void positionAtDoneScan(
838     BTreeRowPosition pos)
839         throws StandardException
840     {
841
842         // Need to do this unlock in any case, until lock manager provides
843
// a way to release locks associated with a compatibility space. This
844
// scan lock is special, as it is a lock on the btree container rather
845
// than the heap container. The open container on the btree actually
846
// has a null locking policy so the close of that container does not
847
// release this lock, need to explicitly unlock it here or when the
848
// scan is closed as part of the abort the lock will not be released.
849
if (pos.current_scan_pageno != 0)
850         {
851             this.getLockingPolicy().unlockScan(pos.current_scan_pageno);
852             pos.current_scan_pageno = 0;
853         }
854
855         pos.current_slot = Page.INVALID_SLOT_NUMBER;
856         pos.current_rh = null;
857         pos.current_positionKey = null;
858         this.scan_state = SCAN_DONE;
859
860         return;
861     }
862
863
864     /**
865      * process_qualifier - Determine if a row meets all qualifier conditions.
866      * <p>
867      * Check all qualifiers in the qualifier array against row. Return true
868      * if all compares specified by the qualifier array return true, else
869      * return false.
870      * <p>
871      * It is up to caller to make sure qualifier list is non-null.
872      *
873      * @param row The row with the same partial column list as the
874      * row returned by the current scan.
875      *
876      * @exception StandardException Standard exception policy.
877      */

878     protected boolean process_qualifier(
879     DataValueDescriptor[] row)
880         throws StandardException
881     {
882         boolean row_qualifies = true;
883         Qualifier q;
884
885         // Process the 2-d qualifier which is structured as follows:
886
//
887
// A two dimensional array is to be used to pass around a AND's and OR's
888
// in conjunctive normal form (CNF). The top slot of the 2 dimensional
889
// array is optimized for the more frequent where no OR's are present.
890
// The first array slot is always a list of AND's to be treated as
891
// described above for single dimensional AND qualifier arrays. The
892
// subsequent slots are to be treated as AND'd arrays or OR's. Thus
893
// the 2 dimensional array qual[][] argument is to be treated as the
894
// following, note if qual.length = 1 then only the first array is
895
// valid and // it is and an array of and clauses:
896
//
897
// (qual[0][0] and qual[0][0] ... and qual[0][qual[0].length - 1])
898
// and
899
// (qual[1][0] or qual[1][1] ... or qual[1][qual[1].length - 1])
900
// and
901
// (qual[2][0] or qual[2][1] ... or qual[2][qual[2].length - 1])
902
// ...
903
// and
904
// (qual[qual.length - 1][0] or qual[1][1] ... or qual[1][2])
905

906         // First do the qual[0] which is an array of qualifer terms.
907

908         if (SanityManager.DEBUG)
909         {
910             // routine should not be called if there is no qualifier
911
SanityManager.ASSERT(this.init_qualifier != null);
912             SanityManager.ASSERT(this.init_qualifier.length > 0);
913         }
914
915         for (int i = 0; i < this.init_qualifier[0].length; i++)
916         {
917             // process each AND clause
918

919             row_qualifies = false;
920
921             // process each OR clause.
922

923             q = this.init_qualifier[0][i];
924
925             // Get the column from the possibly partial row, of the
926
// q.getColumnId()'th column in the full row.
927
DataValueDescriptor columnValue = row[q.getColumnId()];
928
929             row_qualifies =
930                 columnValue.compare(
931                     q.getOperator(),
932                     q.getOrderable(),
933                     q.getOrderedNulls(),
934                     q.getUnknownRV());
935
936             if (q.negateCompareResult())
937                 row_qualifies = !row_qualifies;
938
939             // Once an AND fails the whole Qualification fails - do a return!
940
if (!row_qualifies)
941                 return(false);
942         }
943
944         // all the qual[0] and terms passed, now process the OR clauses
945

946         for (int and_idx = 1; and_idx < this.init_qualifier.length; and_idx++)
947         {
948             // process each AND clause
949

950             row_qualifies = false;
951
952             if (SanityManager.DEBUG)
953             {
954                 // Each OR clause must be non-empty.
955
SanityManager.ASSERT(this.init_qualifier[and_idx].length > 0);
956             }
957
958             for (int or_idx = 0;
959                  or_idx < this.init_qualifier[and_idx].length; or_idx++)
960             {
961                 // process each OR clause.
962

963                 q = this.init_qualifier[and_idx][or_idx];
964
965                 // Get the column from the possibly partial row, of the
966
// q.getColumnId()'th column in the full row.
967
DataValueDescriptor columnValue = row[q.getColumnId()];
968
969                 row_qualifies =
970                     columnValue.compare(
971                         q.getOperator(),
972                         q.getOrderable(),
973                         q.getOrderedNulls(),
974                         q.getUnknownRV());
975
976                 if (q.negateCompareResult())
977                     row_qualifies = !row_qualifies;
978
979                 // once one OR qualifies the entire clause is TRUE
980
if (row_qualifies)
981                     break;
982             }
983
984             if (!row_qualifies)
985                 break;
986         }
987
988         return(row_qualifies);
989     }
990
991
992     /**
993      * Reposition the scan leaving and reentering the access layer.
994      * <p>
995      * When a scan leaves access it saves the RecordHandle of the record
996      * on the page. There are 2 cases to consider when trying to reposition
997      * the scan when re-entering access:
998      * o ROW has not moved off the page.
999      * If the row has not moved then the RecordHandle we have saved
1000     * away is valid, and we just call RawStore to reposition on that
1001     * RecordHandle (RawStore takes care of the row moving within
1002     * the page).
1003     * o ROW has moved off the page.
1004     * This can only happen in the case of a btree split. In that
1005     * case the splitter will have caused all scans positioned on
1006     * this page within the same transaction to save a copy of the
1007     * row that the scan was positioned on. Then to reposition the
1008     * scan it is necessary to research the tree from the top using
1009     * the copy of the row.
1010     *
1011     * If the scan has saved it's position by key (and thus has given up the
1012     * scan lock on the page), there are a few cases where it is possible that
1013     * the key no longer exists in the table. In the case of a scan held
1014     * open across commit it is easy to imagine that the row the scan was
1015     * positioned on could be deleted and subsequently purged from the table
1016     * all before the scan resumes. Also in the case of read uncommitted
1017     * the scan holds no lock on the current row, so it could be purged -
1018     * in the following scenario for instance: read uncommitted transaction 1
1019     * opens scan and positions on row (1,2), transaction 2 deletes (1,2) and
1020     * commits, transaction 1 inserts (1,3) which goes to same page as (1,2)
1021     * and is going to cause a split, transaction 1 saves scan position as
1022     * key, gives up scan lock and then purges row (1, 2), when transaction
1023     * 1 resumes scan (1, 2) no longer exists. missing_row_for_key_ok
1024     * parameter is added as a sanity check to make sure it ok that
1025     * repositioning does not go to same row that we were repositioned on.
1026     *
1027     *
1028     *
1029     * @param pos position to set the scan to.
1030     *
1031     * @param missing_row_for_key_ok if true and exact key is not found then
1032     * scan is just set to key just left of
1033     * the key (thus a next will move to the
1034     * key just after "pos")
1035     *
1036     * @return returns true if scan has been repositioned successfully, else
1037     * returns false if the position key could not be found and
1038     * missing_row_for_key_ok was false indicating that scan could
1039     * only be positioned on the exact key match.
1040     *
1041     * @exception StandardException Standard exception policy.
1042     **/

1043    protected boolean reposition(
1044    BTreeRowPosition pos,
1045    boolean missing_row_for_key_ok)
1046        throws StandardException
1047    {
1048        // RESOLVE (mikem) - performance - we need to do a buffer manager
1049
// get for every row returned from the scan. It may be better to
1050
// allow a reference to the page with no latch (ie. a fixed bit).
1051

1052        if (this.scan_state != SCAN_INPROGRESS)
1053        {
1054            throw StandardException.newException(
1055                SQLState.BTREE_SCAN_NOT_POSITIONED,
1056                new Integer JavaDoc(this.scan_state));
1057        }
1058
1059        // Either current_rh or positionKey is valid - the other is null.
1060
if (SanityManager.DEBUG)
1061        {
1062            if ((pos.current_rh == null) != (pos.current_positionKey != null))
1063                SanityManager.THROWASSERT(
1064                    "pos.current_rh = (" + pos.current_rh + "), " +
1065                    "pos.current_positionKey = (" +
1066                    pos.current_positionKey + ").");
1067        }
1068
1069        if (!((pos.current_rh == null) == (pos.current_positionKey != null)))
1070        {
1071            throw StandardException.newException(
1072                    SQLState.BTREE_SCAN_INTERNAL_ERROR,
1073                    new Boolean JavaDoc(pos.current_rh == null),
1074                    new Boolean JavaDoc(pos.current_positionKey == null));
1075        }
1076
1077        if (pos.current_positionKey == null)
1078        {
1079            // Reposition to remembered spot on page.
1080
if (SanityManager.DEBUG)
1081                SanityManager.ASSERT(pos.current_scan_pageno != 0);
1082
1083            pos.current_leaf = (LeafControlRow)
1084                ControlRow.Get(this, pos.current_rh.getPageNumber());
1085            pos.current_slot =
1086                pos.current_leaf.page.getSlotNumber(pos.current_rh);
1087        }
1088        else
1089        {
1090            // RESOLVE (mikem) - not sure but someday in the future this
1091
// assert may not be true, but for now we always release the
1092
// scan lock when we save the row away as the current position.
1093
if (SanityManager.DEBUG)
1094                SanityManager.ASSERT(pos.current_scan_pageno == 0);
1095
1096            SearchParameters sp =
1097                new SearchParameters(
1098                    pos.current_positionKey,
1099                    // this is a full key search, so this arg is not used.
1100
SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH,
1101                    init_template, this, false);
1102
1103            // latch/lock loop, continue until you can get scan lock on page
1104
// while holding page latched without waiting.
1105

1106
1107            boolean latch_released;
1108            do
1109            {
1110                pos.current_leaf = (LeafControlRow)
1111                    ControlRow.Get(this, BTree.ROOTPAGEID).search(sp);
1112
1113                if (sp.resultExact || missing_row_for_key_ok)
1114                {
1115                    // RESOLVE (mikem) - we could have a scan which always
1116
// maintained it's position by key value, or we could
1117
// optimize and delay this lock until we were about to
1118
// give up the latch. But it is VERY likely we will get
1119
// the lock since we have the latch on the page.
1120
//
1121
// In order to be successfully positioned we must get the
1122
// scan lock again.
1123
latch_released =
1124                        !this.getLockingPolicy().lockScan(
1125                            pos.current_leaf,
1126                            (LeafControlRow) null, // no other latch currently
1127
false /* not for update */,
1128                            ConglomerateController.LOCK_READ); // read lock on scan position
1129

1130                    // TESTING CODE:
1131
if (SanityManager.DEBUG)
1132                    {
1133                        latch_released =
1134                            test_errors(
1135                                this,
1136                                "BTreeScan_reposition", true,
1137                                this.getLockingPolicy(),
1138                                pos.current_leaf, latch_released);
1139                    }
1140                }
1141                else
1142                {
1143                    // Did not find key to exactly position on.
1144

1145                    pos.current_leaf.release();
1146                    pos.current_leaf = null;
1147                    return(false);
1148                }
1149
1150            } while (latch_released);
1151
1152            pos.current_scan_pageno = pos.current_leaf.page.getPageNumber();
1153            pos.current_slot = sp.resultSlot;
1154            pos.current_positionKey = null;
1155        }
1156
1157        return(true);
1158    }
1159
1160    /*
1161    ** Public Methods of BTreeScan
1162    */

1163
1164
1165    /**
1166    Initialize the scan for use.
1167    <p>
1168    Any changes to this method may have to be reflected in close as well.
1169    <p>
1170    The btree init opens the container (super.init), and stores away the
1171    state of the qualifiers. The actual searching for the first position
1172    is delayed until the first next() call.
1173
1174    @exception StandardException Standard exception policy.
1175    **/

1176    public void init(
1177    TransactionManager xact_manager,
1178    Transaction rawtran,
1179    boolean hold,
1180    int open_mode,
1181    int lock_level,
1182    BTreeLockingPolicy btree_locking_policy,
1183    FormatableBitSet scanColumnList,
1184    DataValueDescriptor[] startKeyValue,
1185    int startSearchOperator,
1186    Qualifier qualifier[][],
1187    DataValueDescriptor[] stopKeyValue,
1188    int stopSearchOperator,
1189    BTree conglomerate,
1190    LogicalUndo undo,
1191    StaticCompiledOpenConglomInfo static_info,
1192    DynamicCompiledOpenConglomInfo dynamic_info)
1193        throws StandardException
1194    {
1195        super.init(
1196            xact_manager, xact_manager, (ContainerHandle) null, rawtran,
1197            hold,
1198            open_mode, lock_level, btree_locking_policy,
1199            conglomerate, undo, dynamic_info);
1200
1201
1202        this.init_rawtran = rawtran;
1203        this.init_forUpdate =
1204            ((open_mode & ContainerHandle.MODE_FORUPDATE) ==
1205                 ContainerHandle.MODE_FORUPDATE);
1206
1207        // Keep track of whether this scan should use update locks.
1208
this.init_useUpdateLocks =
1209            ((open_mode &
1210                ContainerHandle.MODE_USE_UPDATE_LOCKS) != 0);
1211
1212        this.init_hold = hold;
1213
1214        this.init_template = runtime_mem.get_template();
1215
1216        this.init_scanColumnList = scanColumnList;
1217
1218        this.init_lock_fetch_desc =
1219            RowUtil.getFetchDescriptorConstant(init_template.length - 1);
1220
1221        if (SanityManager.DEBUG)
1222        {
1223            SanityManager.ASSERT(
1224                init_lock_fetch_desc.getMaxFetchColumnId() ==
1225                    (init_template.length - 1));
1226            SanityManager.ASSERT(
1227                (init_lock_fetch_desc.getValidColumnsArray())[init_template.length - 1] == 1);
1228        }
1229
1230        // note that we don't process qualifiers in btree fetch's
1231
this.init_fetchDesc =
1232            new FetchDescriptor(
1233                init_template.length, init_scanColumnList,(Qualifier[][]) null);
1234
1235        initScanParams(
1236            startKeyValue, startSearchOperator,
1237            qualifier, stopKeyValue, stopSearchOperator);
1238
1239        
1240        if (SanityManager.DEBUG)
1241        {
1242            // RESOLVE - (mikem) we should we require a template, need to
1243
// clean up some of the old tests which did not provide one?
1244
if (init_template != null)
1245            {
1246                SanityManager.ASSERT(
1247                    TemplateRow.checkColumnTypes(
1248                        this.getConglomerate().format_ids, init_template));
1249            }
1250        }
1251
1252        // System.out.println("initializing scan:" + this);
1253

1254        // initialize default locking operation for the scan.
1255
this.lock_operation =
1256            (init_forUpdate ?
1257                ConglomerateController.LOCK_UPD :
1258                ConglomerateController.LOCK_READ);
1259
1260        if (init_useUpdateLocks)
1261            this.lock_operation |= ConglomerateController.LOCK_UPDATE_LOCKS;
1262
1263        // System.out.println("Btree scan: " + this);
1264
}
1265
1266
1267
1268    /*
1269    ** Methods of ScanController
1270    */

1271
1272    /**
1273    Close the scan.
1274    **/

1275    public void close()
1276        throws StandardException
1277    {
1278        // Scan is closed, make sure no access to any state variables
1279
positionAtDoneScanFromClose(scan_position);
1280
1281        super.close();
1282
1283        // null out so that these object's can get GC'd earlier.
1284
this.init_rawtran = null;
1285        this.init_template = null;
1286        this.init_startKeyValue = null;
1287        this.init_qualifier = null;
1288        this.init_stopKeyValue = null;
1289
1290        this.getXactMgr().closeMe(this);
1291    }
1292
1293    /**
1294    Delete the row at the current position of the scan.
1295    @see ScanController#delete
1296
1297    @exception StandardException Standard exception policy.
1298    **/

1299    public boolean delete()
1300        throws StandardException
1301    {
1302        boolean ret_val = false;
1303
1304        if (scan_state != SCAN_INPROGRESS)
1305            throw StandardException.newException(
1306                SQLState.AM_SCAN_NOT_POSITIONED);
1307
1308        if (SanityManager.DEBUG)
1309        {
1310            SanityManager.ASSERT(this.container != null,
1311                "BTreeScan.delete() called on a closed scan.");
1312            SanityManager.ASSERT(init_forUpdate);
1313        }
1314
1315        try
1316        {
1317            // Get current page of scan, with latch.
1318
if (!reposition(scan_position, false))
1319            {
1320                throw StandardException.newException(
1321                        SQLState.AM_RECORD_NOT_FOUND,
1322                        new Long JavaDoc(err_containerid),
1323                        new Long JavaDoc(scan_position.current_rh.getId()));
1324            }
1325
1326
1327            if (init_useUpdateLocks)
1328            {
1329                // RESOLVE (mikem) - I don't think lockScanRow() is the right
1330
// thing to call.
1331

1332                // if we are doing update locking, then we got an U lock on
1333
// this row when the scan positioned on it, but now that we
1334
// are doing a delete on the current position we need to upgrade
1335
// the lock to X.
1336
boolean latch_released =
1337                    !this.getLockingPolicy().lockScanRow(
1338                        this, this.getConglomerate(), scan_position,
1339                        false,
1340                        init_lock_fetch_desc,
1341                        scan_position.current_lock_template,
1342                        scan_position.current_lock_row_loc,
1343                        false, init_forUpdate, lock_operation);
1344
1345                if (latch_released)
1346                {
1347                    // lost latch on page in order to wait for row lock.
1348
// Because we have scan lock on page, we need only
1349
// call reposition() which will use the saved record
1350
// handle to reposition to the same spot on the page.
1351
// We don't have to search the
1352
// tree again, as we have the a scan lock on the page
1353
// which means the current_rh is valid to reposition on.
1354
if (reposition(scan_position, false))
1355                    {
1356                        throw StandardException.newException(
1357                                SQLState.AM_RECORD_NOT_FOUND,
1358                                new Long JavaDoc(err_containerid),
1359                                new Long JavaDoc(scan_position.current_rh.getId()));
1360                    }
1361                }
1362            }
1363
1364
1365            // Do a fetch just to get the RecordHandle for the delete call,
1366
// don't fetch any columns.
1367
RecordHandle delete_rh =
1368                scan_position.current_leaf.page.fetchFromSlot(
1369                    (RecordHandle) null, scan_position.current_slot,
1370                    RowUtil.EMPTY_ROW, (FetchDescriptor) null, true);
1371
1372            ret_val =
1373                scan_position.current_leaf.page.delete(
1374                    delete_rh, this.btree_undo);
1375
1376            // See if we just deleted the last row on the page, in a btree a
1377
// page with all rows still has 1 left - the control row.
1378
// Beetle 5750: we do not reclaim the root page of the btree if
1379
// there are no children since we were
1380
// doing too many post commit actions in a benchmark which does an
1381
// insert/commit/delete/commit operations in a single user system. now ,
1382
// with this change the work will move to the user
1383
// thread which does the insert
1384

1385            if (scan_position.current_leaf.page.nonDeletedRecordCount() == 1 &&
1386        !(scan_position.current_leaf.getIsRoot() &&
1387          scan_position.current_leaf.getLevel() == 0 ))
1388            {
1389                this.getXactMgr().addPostCommitWork(new BTreePostCommit(
1390                    this.getXactMgr().getAccessManager(),
1391                    this.getConglomerate(),
1392                    scan_position.current_leaf.page.getPageNumber()));
1393            }
1394        }
1395        finally
1396        {
1397            if (scan_position.current_leaf != null)
1398            {
1399                // release latch on page
1400
scan_position.current_leaf.release();
1401                scan_position.current_leaf = null;
1402            }
1403        }
1404
1405        return(ret_val);
1406    }
1407
1408    /**
1409     * A call to allow client to indicate that current row does not qualify.
1410     * <p>
1411     * Indicates to the ScanController that the current row does not
1412     * qualify for the scan. If the isolation level of the scan allows,
1413     * this may result in the scan releasing the lock on this row.
1414     * <p>
1415     * Note that some scan implimentations may not support releasing locks on
1416     * non-qualifying rows, or may delay releasing the lock until sometime
1417     * later in the scan (ie. it may be necessary to keep the lock until
1418     * either the scan is repositioned on the next row or page).
1419     * <p>
1420     * This call should only be made while the scan is positioned on a current
1421     * valid row.
1422     *
1423     * @exception StandardException Standard exception policy.
1424     **/

1425    public void didNotQualify()
1426        throws StandardException
1427    {
1428    }
1429
1430
1431    /**
1432     * Returns true if the current position of the scan still qualifies
1433     * under the set of qualifiers passed to the openScan(). When called
1434     * this routine will reapply all qualifiers against the row currently
1435     * positioned and return true if the row still qualifies. If the row
1436     * has been deleted or no longer passes the qualifiers then this routine
1437     * will return false.
1438     * <p>
1439     * This case can come about if the current scan
1440     * or another scan on the same table in the same transaction
1441     * deleted the row or changed columns referenced by the qualifier after
1442     * the next() call which positioned the scan at this row.
1443     * <p>
1444     * Note that for comglomerates which don't support update, like btree's,
1445     * there is no need to recheck the qualifiers.
1446     * <p>
1447     * The results of a fetch() performed on a scan positioned on
1448     * a deleted row are undefined.
1449     * <p>
1450     * @exception StandardException Standard exception policy.
1451    **/

1452    public boolean doesCurrentPositionQualify()
1453        throws StandardException
1454    {
1455        if (scan_state != SCAN_INPROGRESS)
1456            throw StandardException.newException(
1457                SQLState.AM_SCAN_NOT_POSITIONED);
1458
1459        if (SanityManager.DEBUG)
1460        {
1461            SanityManager.ASSERT(this.container != null,
1462            "BTreeScan.doesCurrentPositionQualify() called on a closed scan.");
1463        }
1464
1465        try
1466        {
1467            // Get current page of scan, with latch
1468
if (!reposition(scan_position, false))
1469            {
1470                // TODO - write unit test to get here, language always calls
1471
// isCurrentPositionDeleted() right before calling this, so
1472
// hard to write .sql test to exercise this.
1473

1474                // if reposition fails it means the position of the scan
1475
// has been purged from the table - for example if this is
1476
// a uncommitted read scan and somehow the row was purged
1477
// since the last positioning.
1478

1479                return(false);
1480            }
1481
1482            if (SanityManager.DEBUG)
1483            {
1484                SanityManager.ASSERT(
1485                    scan_position.current_leaf.page.fetchNumFieldsAtSlot(
1486                        scan_position.current_slot) > 1);
1487            }
1488
1489            // Since btree row don't get updated, the only way a current
1490
// position may not qualify is if it got deleted.
1491
return(
1492                !scan_position.current_leaf.page.isDeletedAtSlot(
1493                    scan_position.current_slot));
1494        }
1495        finally
1496        {
1497
1498            if (scan_position.current_leaf != null)
1499            {
1500                // release latch on page.
1501
scan_position.current_leaf.release();
1502                scan_position.current_leaf = null;
1503            }
1504        }
1505    }
1506
1507
1508    /**
1509     * Fetch the row at the current position of the Scan.
1510     *
1511     * @param row The row into which the value of the current
1512     * position in the scan is to be stored.
1513     * @param qualify indicates whether the qualifiers should be applied.
1514     *
1515     * @exception StandardException Standard exception policy.
1516     */

1517    private void fetch(DataValueDescriptor[] row, boolean qualify)
1518        throws StandardException
1519    {
1520        if (scan_state != SCAN_INPROGRESS)
1521            throw StandardException.newException(
1522                SQLState.AM_SCAN_NOT_POSITIONED);
1523        if (SanityManager.DEBUG)
1524        {
1525            SanityManager.ASSERT(this.container != null,
1526                "BTreeScan.fetch() called on a closed scan.");
1527            
1528            TemplateRow.checkPartialColumnTypes(
1529                this.getConglomerate().format_ids,
1530                init_scanColumnList, (int []) null, row);
1531        }
1532
1533        try
1534        {
1535            // Get current page of scan, with latch
1536
if (!reposition(scan_position, false))
1537            {
1538                // TODO - write unit test to get here, language always calls
1539
// isCurrentPositionDeleted() right before calling this, so
1540
// hard to write .sql test to exercise this.
1541

1542                throw StandardException.newException(
1543                        SQLState.AM_RECORD_NOT_FOUND,
1544                        new Long JavaDoc(err_containerid),
1545                        new Long JavaDoc(scan_position.current_rh.getId()));
1546            }
1547
1548            if (SanityManager.DEBUG)
1549            {
1550                SanityManager.ASSERT(
1551                    scan_position.current_leaf.page.fetchNumFieldsAtSlot(
1552                        scan_position.current_slot) > 1);
1553            }
1554
1555            scan_position.current_rh =
1556                scan_position.current_leaf.page.fetchFromSlot(
1557                (RecordHandle) null,
1558                scan_position.current_slot, row,
1559                qualify ? init_fetchDesc : null,
1560                true);
1561
1562            // The possibility is that the row at the current position
1563
// has been marked as deleted (it cannot have been purged
1564
// since the scan maintains a lock on the row, and purges
1565
// are always done from system transactions). I'm not sure
1566
// what the desired behavior is in this case. For now,
1567
// just return null.
1568

1569            // RESOLVE (mikem) - what should be done here?
1570
if (scan_position.current_leaf.page.isDeletedAtSlot(
1571                    scan_position.current_slot))
1572            {
1573                if (SanityManager.DEBUG)
1574                    SanityManager.ASSERT(false, "positioned on deleted row");
1575            }
1576        }
1577        finally
1578        {
1579            if (scan_position.current_leaf != null)
1580            {
1581                // release latch on page.
1582
scan_position.current_leaf.release();
1583                scan_position.current_leaf = null;
1584            }
1585        }
1586
1587        return;
1588    }
1589
1590    /**
1591    Fetch the row at the current position of the Scan.
1592    @see ScanController#fetch
1593
1594    @exception StandardException Standard exception policy.
1595    **/

1596    public void fetch(DataValueDescriptor[] row)
1597        throws StandardException
1598    {
1599        fetch(row, true);
1600    }
1601
1602    /**
1603     * Fetch the row at the current position of the Scan without applying the
1604     * qualifiers.
1605     * @see ScanController#fetchWithoutQualify
1606     *
1607     * @exception StandardException Standard exception policy.
1608     */

1609    public void fetchWithoutQualify(DataValueDescriptor[] row)
1610        throws StandardException
1611    {
1612        fetch(row, false);
1613    }
1614    
1615    /**
1616     * Return ScanInfo object which describes performance of scan.
1617     * <p>
1618     * Return ScanInfo object which contains information about the current
1619     * scan.
1620     * <p>
1621     *
1622     * @see ScanInfo
1623     *
1624     * @return The ScanInfo object which contains info about current scan.
1625     *
1626     * @exception StandardException Standard exception policy.
1627     **/

1628    public ScanInfo getScanInfo()
1629        throws StandardException
1630    {
1631        return(new BTreeScanInfo(this));
1632    }
1633
1634    /**
1635    Returns true if the current position of the scan is at a
1636    deleted row. This case can come about if the current scan
1637    or another scan on the same table in the same transaction
1638    deleted the row after the next() call which positioned the
1639    scan at this row.
1640
1641    The results of a fetch() performed on a scan positioned on
1642    a deleted row are undefined.
1643
1644    @exception StandardException Standard exception policy.
1645    **/

1646    public boolean isCurrentPositionDeleted()
1647        throws StandardException
1648    {
1649        boolean ret_val;
1650
1651        if (scan_state != SCAN_INPROGRESS)
1652            throw StandardException.newException(
1653                SQLState.AM_SCAN_NOT_POSITIONED);
1654
1655        if (SanityManager.DEBUG)
1656        {
1657            SanityManager.ASSERT(this.container != null,
1658                "BTreeScan.isCurrentPositionDeleted() called on closed scan.");
1659        }
1660        try
1661        {
1662            // Get current page of scan, with latch
1663

1664            if (reposition(scan_position, false))
1665            {
1666
1667                if (SanityManager.DEBUG)
1668                {
1669                    SanityManager.ASSERT(
1670                        scan_position.current_leaf.page.fetchNumFieldsAtSlot(
1671                            scan_position.current_slot) > 1);
1672                }
1673
1674                ret_val =
1675                    scan_position.current_leaf.page.isDeletedAtSlot(
1676                        scan_position.current_slot);
1677            }
1678            else
1679            {
1680                ret_val = false;
1681            }
1682        }
1683        finally
1684        {
1685            if (scan_position.current_leaf != null)
1686            {
1687                // release latch on page.
1688
scan_position.current_leaf.release();
1689                scan_position.current_leaf = null;
1690            }
1691        }
1692
1693        return(ret_val);
1694    }
1695
1696    /**
1697     * Return whether this is a keyed conglomerate.
1698     * <p>
1699     *
1700     * @return whether this is a keyed conglomerate.
1701     **/

1702    public boolean isKeyed()
1703    {
1704        return(true);
1705    }
1706
1707    /**
1708     * @see ScanController#positionAtRowLocation
1709     *
1710     * Not implemented for this class
1711     */

1712    public boolean positionAtRowLocation (RowLocation rLoc)
1713        throws StandardException
1714    {
1715        throw StandardException.newException(
1716                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1717    }
1718
1719    /**
1720    Move to the next position in the scan.
1721    @see ScanController#next
1722
1723    @exception StandardException Standard exception policy.
1724    **/

1725    public boolean next()
1726        throws StandardException
1727    {
1728        // Turn this call into a group fetch of a 1 element group.
1729
fetchNext_one_slot_array[0] = runtime_mem.get_scratch_row();
1730        boolean ret_val =
1731            fetchRows(
1732                scan_position,
1733                fetchNext_one_slot_array,
1734                (RowLocation[]) null,
1735                (BackingStoreHashtable) null,
1736                1,
1737                (int[]) null) == 1;
1738
1739
1740        return(ret_val);
1741    }
1742
1743    /**
1744    Fetch the row at the next position of the Scan.
1745
1746    If there is a valid next position in the scan then
1747    the value in the template storable row is replaced
1748    with the value of the row at the current scan
1749    position. The columns of the template row must
1750    be of the same type as the actual columns in the
1751    underlying conglomerate.
1752
1753    The resulting contents of templateRow after a fetchNext()
1754    which returns false is undefined.
1755
1756    The result of calling fetchNext(row) is exactly logically
1757    equivalent to making a next() call followed by a fetch(row)
1758    call. This interface allows implementations to optimize
1759    the 2 calls if possible.
1760
1761    @param row The template row into which the value
1762    of the next position in the scan is to be stored.
1763
1764    @return True if there is a next position in the scan,
1765    false if there isn't.
1766
1767    @exception StandardException Standard exception policy.
1768    **/

1769    public boolean fetchNext(DataValueDescriptor[] row)
1770        throws StandardException
1771    {
1772        boolean ret_val;
1773
1774        if (SanityManager.DEBUG)
1775        {
1776            TemplateRow.checkPartialColumnTypes(
1777                this.getConglomerate().format_ids,
1778                init_scanColumnList, (int[]) null, row);
1779        }
1780
1781        // Turn this call into a group fetch of a 1 element group.
1782
fetchNext_one_slot_array[0] = row;
1783        ret_val =
1784            fetchRows(
1785                scan_position,
1786                fetchNext_one_slot_array,
1787                (RowLocation[]) null,
1788                (BackingStoreHashtable) null,
1789                1,
1790                (int[]) null) == 1;
1791
1792        return(ret_val);
1793    }
1794
1795    /**
1796     * Fetch the next N rows from the table.
1797     * <p>
1798     * The client allocates an array of N rows and passes it into the
1799     * fetchNextSet() call. This routine does the equivalent of N
1800     * fetchNext() calls, filling in each of the rows in the array.
1801     * Locking is performed exactly as if the N fetchNext() calls had
1802     * been made.
1803     * <p>
1804     * It is up to Access how many rows to return. fetchNextSet() will
1805     * return how many rows were filled in. If fetchNextSet() returns 0
1806     * then the scan is complete, (ie. the scan is in the same state as if
1807     * fetchNext() had returned false). If the scan is not complete then
1808     * fetchNext() will return (1 <= row_count <= N).
1809     * <p>
1810     * The current position of the scan is undefined if fetchNextSet()
1811     * is used (ie. mixing fetch()/fetchNext() and fetchNextSet() calls
1812     * in a single scan does not work). This is because a fetchNextSet()
1813     * request for 5 rows from a heap where the first 2 rows qualify, but
1814     * no other rows qualify will result in the scan being positioned at
1815     * the end of the table, while if 5 rows did qualify the scan will be
1816     * positioned on the 5th row.
1817     * <p>
1818     * Qualifiers, start and stop positioning of the openscan are applied
1819     * just as in a normal scan.
1820     * <p>
1821     * The columns of the row will be the standard columns returned as
1822     * part of a scan, as described by the validColumns - see openScan for
1823     * description.
1824     * <p>
1825     * Expected usage:
1826     *
1827     * // allocate an array of 5 empty row templates
1828     * DataValueDescriptor[][] row_array = allocate_row_array(5);
1829     * int row_cnt = 0;
1830     *
1831     * scan = openScan();
1832     *
1833     * while ((row_cnt = scan.fetchNextSet(row_array) != 0)
1834     * {
1835     * // I got "row_cnt" rows from the scan. These rows will be
1836     * // found in row_array[0] through row_array[row_cnt - 1]
1837     * }
1838     *
1839     * <p>
1840     *
1841     * RESOLVE - This interface is being provided so that we can prototype
1842     * the performance results it can achieve. If it looks like
1843     * this interface is useful, it is very likely we will look
1844     * into a better way to tie together the now 4 different
1845     * fetch interfaces: fetch, fetchNext(), fetchNextGroup(),
1846     * and fetchSet().
1847     *
1848     * @return The number of qualifying rows found and copied into the
1849     * provided array of rows. If 0 then the scan is complete,
1850     * otherwise the return value will be:
1851     * 1 <= row_count <= row_array.length
1852     *
1853     * @param row_array The array of rows to copy rows into.
1854     * row_array[].length must >= 1. This routine
1855     * assumes that all entries in the array
1856     * contain complete template rows.
1857     *
1858     * @exception StandardException Standard exception policy.
1859     **/

1860    public int fetchNextGroup(
1861    DataValueDescriptor[][] row_array,
1862    RowLocation[] rowloc_array)
1863        throws StandardException
1864    {
1865        return(
1866            fetchRows(
1867                scan_position,
1868                row_array,
1869                rowloc_array,
1870                (BackingStoreHashtable) null,
1871                row_array.length,
1872                (int[]) null));
1873    }
1874
1875    public int fetchNextGroup(
1876    DataValueDescriptor[][] row_array,
1877    RowLocation[] old_rowloc_array,
1878    RowLocation[] new_rowloc_array)
1879        throws StandardException
1880    {
1881        // This interface is currently only used to move rows around in
1882
// a heap table, unused in btree's -- so not implemented.
1883

1884        throw StandardException.newException(
1885                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
1886    }
1887
1888    /**
1889     * Insert all rows that qualify for the current scan into the input
1890     * Hash table.
1891     * <p>
1892     * This routine scans executes the entire scan as described in the
1893     * openScan call. For every qualifying unique row value an entry is
1894     * placed into the HashTable. For unique row values the entry in the
1895     * BackingStoreHashtable has a key value of the object stored in
1896     * row[key_column_number], and the value of the data is row. For row
1897     * values with duplicates, the key value is also row[key_column_number],
1898     * but the value of the data is a Vector of
1899     * rows. The caller will have to call "instanceof" on the data value
1900     * object if duplicates are expected, to determine if the data value
1901     * of the Hashtable entry is a row or is a Vector of rows.
1902     * <p>
1903     * Note, that for this routine to work efficiently the caller must
1904     * ensure that the object in row[key_column_number] implements
1905     * the hashCode and equals method as appropriate for it's datatype.
1906     * <p>
1907     * It is expected that this call will be the first and only call made in
1908     * an openscan. Qualifiers and stop position of the openscan are applied
1909     * just as in a normal scan. This call is logically equivalent to the
1910     * caller performing the following:
1911     *
1912     * import java.util.Hashtable;
1913     *
1914     * hash_table = new Hashtable();
1915     *
1916     * while (next())
1917     * {
1918     * row = create_new_row();
1919     * fetch(row);
1920     * if ((duplicate_value =
1921     * hash_table.put(row[key_column_number], row)) != null)
1922     * {
1923     * Vector row_vec;
1924     *
1925     * // inserted a duplicate
1926     * if ((duplicate_value instanceof vector))
1927     * {
1928     * row_vec = (Vector) duplicate_value;
1929     * }
1930     * else
1931     * {
1932     * // allocate vector to hold duplicates
1933     * row_vec = new Vector(2);
1934     *
1935     * // insert original row into vector
1936     * row_vec.addElement(duplicate_value);
1937     *
1938     * // put the vector as the data rather than the row
1939     * hash_table.put(row[key_column_number], row_vec);
1940     * }
1941     *
1942     * // insert new row into vector
1943     * row_vec.addElement(row);
1944     * }
1945     * }
1946     * <p>
1947     * The columns of the row will be the standard columns returned as
1948     * part of a scan, as described by the validColumns - see openScan for
1949     * description.
1950     * RESOLVE - is this ok? or should I hard code somehow the row to
1951     * be the first column and the row location?
1952     * <p>
1953     * Currently it is only possible to hash on the first column in the
1954     * conglomerate, in the future we may change the interface to allow
1955     * hashing either on a different column or maybe on a combination of
1956     * columns.
1957     * <p>
1958     * No overflow to external storage is provided, so calling this routine
1959     * on a 1 gigabyte conglomerate will incur at least 1 gigabyte of memory
1960     * (probably failing with a java out of memory condition). If this
1961     * routine gets an out of memory condition, or if "max_rowcnt" is
1962     * exceeded then then the routine will give up, empty the Hashtable,
1963     * and return "false."
1964     * <p>
1965     * On exit from this routine, whether the fetchSet() succeeded or not
1966     * the scan is complete, it is positioned just the same as if the scan
1967     * had been drained by calling "next()" until it returns false (ie.
1968     * fetchNext() and next() calls will return false).
1969     * reopenScan() can be called to restart the scan.
1970     * <p>
1971     *
1972     * RESOLVE - until we get row counts what should we do for sizing the
1973     * the size, capasity, and load factor of the hash table.
1974     * For now it is up to the caller to create the Hashtable,
1975     * Access does not reset any parameters.
1976     * <p>
1977     * RESOLVE - I am not sure if access should be in charge of allocating
1978     * the new row objects. I know that I can do this in the
1979     * case of btree's, but I don't think I can do this in heaps.
1980     * Maybe this is solved by work to be done on the sort
1981     * interface.
1982     *
1983     *
1984     * @param max_rowcnt The maximum number of rows to insert into the
1985     * Hash table. Pass in -1 if there is no maximum.
1986     * @param key_column_numbers The column numbers of the columns in the
1987     * scan result row to be the key to the Hashtable.
1988     * "0" is the first column in the scan result
1989     * row (which may be different than the first
1990     * column in the row in the table of the scan).
1991     * @param hash_table The java HashTable to load into.
1992     *
1993     * @exception StandardException Standard exception policy.
1994     **/

1995    public void fetchSet(
1996    long max_rowcnt,
1997    int[] key_column_numbers,
1998    BackingStoreHashtable hash_table)
1999        throws StandardException
2000    {
2001        // System.out.println("fetchSet");
2002

2003        fetchRows(
2004            scan_position,
2005            (DataValueDescriptor[][]) null,
2006            (RowLocation[]) null,
2007            (BackingStoreHashtable) hash_table,
2008            max_rowcnt,
2009            key_column_numbers);
2010
2011        return;
2012    }
2013
2014
2015    /**
2016    Reposition the current scan. This call is semantically the same as if
2017    the current scan had been closed and a openScan() had been called instead.
2018    The scan is reopened with against the same conglomerate, and the scan
2019    is reopened with the same "hold" and "forUpdate" parameters passed in
2020    the original openScan. The previous template row continues to be used.
2021
2022    @param startKeyValue An indexable row which holds a
2023    (partial) key value which, in combination with the
2024    startSearchOperator, defines the starting position of
2025    the scan. If null, the starting position of the scan
2026    is the first row of the conglomerate.
2027
2028    @param startSearchOperator an operator which defines
2029    how the startKeyValue is to be searched for. If
2030    startSearchOperation is ScanController.GE, the scan starts on
2031    the first row which is greater than or equal to the
2032    startKeyValue. If startSearchOperation is ScanController.GT,
2033    the scan starts on the first row whose key is greater than
2034    startKeyValue. The startSearchOperation parameter is
2035    ignored if the startKeyValue parameter is null.
2036
2037    @param qualifier An array of qualifiers which, applied
2038    to each key, restrict the rows returned by the scan. Rows
2039    for which any one of the qualifiers returns false are not
2040    returned by the scan. If null, all rows are returned.
2041
2042    @param stopKeyValue An indexable row which holds a
2043    (partial) key value which, in combination with the
2044    stopSearchOperator, defines the ending position of
2045    the scan. If null, the ending position of the scan
2046    is the last row of the conglomerate.
2047
2048    @param stopSearchOperator an operator which defines
2049    how the stopKeyValue is used to determine the scan stopping
2050    position. If stopSearchOperation is ScanController.GE, the scan
2051    stops just before the first row which is greater than or
2052    equal to the stopKeyValue. If stopSearchOperation is
2053    ScanController.GT, the scan stops just before the first row whose
2054    key is greater than startKeyValue. The stopSearchOperation
2055    parameter is ignored if the stopKeyValue parameter is null.
2056
2057    @exception StandardException Standard exception policy.
2058    **/

2059    public final void reopenScan(
2060    DataValueDescriptor[] startKeyValue,
2061    int startSearchOperator,
2062    Qualifier qualifier[][],
2063    DataValueDescriptor[] stopKeyValue,
2064    int stopSearchOperator)
2065        throws StandardException
2066    {
2067        if (SanityManager.DEBUG)
2068        {
2069            if (!init_hold)
2070                SanityManager.ASSERT(this.container != null,
2071                    "BTreeScan.reopenScan() called on non-held closed scan.");
2072
2073            // should only be called by clients outside of store, so should
2074
// not be possible for a latch to held.
2075
SanityManager.ASSERT(scan_position.current_leaf == null);
2076        }
2077
2078        // call unlockScanRecordAfterRead() before setting the scan back
2079
// to init state, so that we release the last lock if necessary (ie.
2080
// for read committed).
2081
//
2082

2083        if (scan_position.current_rh != null)
2084        {
2085            // reposition to get record handle if we don't have it.
2086

2087            if (!reposition(scan_position, false))
2088            {
2089                if (SanityManager.DEBUG)
2090                {
2091                    SanityManager.THROWASSERT(
2092                        "can not fail while holding update row lock.");
2093                }
2094            }
2095
2096            this.getLockingPolicy().unlockScanRecordAfterRead(
2097                scan_position, init_forUpdate);
2098
2099            scan_position.current_rh = null;
2100            scan_position.current_leaf.release();
2101            scan_position.current_leaf = null;
2102        }
2103
2104
2105        // Need to do this unlock in any case, until lock manager provides
2106
// a way to release locks associated with a compatibility space. This
2107
// scan lock is special, as it is a lock on the btree container rather
2108
// than the heap container. The open container on the btree actually
2109
// has a null locking policy so the close of that container does not
2110
// release this lock, need to explicitly unlock it here or when the
2111
// scan is closed as part of the abort the lock will not be released.
2112
if (scan_position.current_scan_pageno != 0)
2113        {
2114            this.getLockingPolicy().unlockScan(
2115                scan_position.current_scan_pageno);
2116            scan_position.current_scan_pageno = 0;
2117        }
2118
2119        scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
2120        scan_position.current_rh = null;
2121        scan_position.current_positionKey = null;
2122
2123        initScanParams(
2124            startKeyValue, startSearchOperator,
2125            qualifier, stopKeyValue, stopSearchOperator);
2126
2127        if (!init_hold)
2128            this.scan_state = SCAN_INIT;
2129        else
2130            this.scan_state =
2131                (this.container != null ? SCAN_INIT : SCAN_HOLD_INIT);
2132    }
2133
2134    /**
2135    Reposition the current scan. This call is semantically the same as if
2136    the current scan had been closed and a openScan() had been called instead.
2137    The scan is reopened against the same conglomerate, and the scan
2138    is reopened with the same "scan column list", "hold" and "forUpdate"
2139    parameters passed in the original openScan.
2140    <p>
2141    The statistics gathered by the scan are not reset to 0 by a reopenScan(),
2142    rather they continue to accumulate.
2143    <p>
2144    Note that this operation is currently only supported on Heap conglomerates.
2145    Also note that order of rows within are heap are not guaranteed, so for
2146    instance positioning at a RowLocation in the "middle" of a heap, then
2147    inserting more data, then continuing the scan is not guaranteed to see
2148    the new rows - they may be put in the "beginning" of the heap.
2149
2150    @param startRowLocation An existing RowLocation within the conglomerate,
2151    at which to position the start of the scan. The scan will begin at this
2152    location and continue forward until the end of the conglomerate.
2153    Positioning at a non-existent RowLocation (ie. an invalid one or one that
2154    had been deleted), will result in an exception being thrown when the
2155    first next operation is attempted.
2156
2157    @param qualifier An array of qualifiers which, applied
2158    to each key, restrict the rows returned by the scan. Rows
2159    for which any one of the qualifiers returns false are not
2160    returned by the scan. If null, all rows are returned.
2161
2162    @exception StandardException Standard exception policy.
2163    **/

2164    public void reopenScanByRowLocation(
2165    RowLocation startRowLocation,
2166    Qualifier qualifier[][])
2167        throws StandardException
2168    {
2169        throw StandardException.newException(
2170                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
2171    }
2172
2173    /*
2174    ** Methods of ScanController, which are not supported by btree.
2175    */

2176
2177    /**
2178    Fetch the location of the current position in the scan.
2179    @see ScanController#fetchLocation
2180
2181    @exception StandardException Standard exception policy.
2182    **/

2183    public void fetchLocation(RowLocation templateLocation)
2184        throws StandardException
2185    {
2186        throw StandardException.newException(
2187                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
2188    }
2189
2190    /**
2191    Return a row location object of the correct type to be
2192    used in calls to fetchLocation.
2193    @see GenericScanController#newRowLocationTemplate
2194
2195    @exception StandardException Standard exception policy.
2196    **/

2197    public RowLocation newRowLocationTemplate()
2198        throws StandardException
2199    {
2200        throw StandardException.newException(
2201                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
2202    }
2203
2204    /**
2205    Replace the entire row at the current position of the scan.
2206
2207    Unimplemented interface by btree, will throw an exception.
2208
2209    @see ScanController#replace
2210    @exception StandardException Standard exception policy.
2211    **/

2212    public boolean replace(DataValueDescriptor[] row, FormatableBitSet validColumns)
2213        throws StandardException
2214    {
2215        throw StandardException.newException(
2216                SQLState.BTREE_UNIMPLEMENTED_FEATURE);
2217    }
2218
2219    /*
2220    ** Methods of ScanManager
2221    */

2222
2223
2224    /**
2225    Close the scan, a commit or abort is about to happen.
2226    **/

2227    public boolean closeForEndTransaction(boolean closeHeldScan)
2228        throws StandardException
2229    {
2230        if (!init_hold || closeHeldScan)
2231        {
2232            // Scan is closed, make sure no access to any state variables
2233
positionAtDoneScan(scan_position);
2234
2235            super.close();
2236
2237            // null out so that these object's can get GC'd earlier.
2238
this.init_rawtran = null;
2239            this.init_template = null;
2240            this.init_startKeyValue = null;
2241            this.init_qualifier = null;
2242            this.init_stopKeyValue = null;
2243
2244            this.getXactMgr().closeMe(this);
2245
2246            return(true);
2247        }
2248        else
2249        {
2250
2251            if (this.scan_state == SCAN_INPROGRESS)
2252            {
2253                if (SanityManager.DEBUG)
2254                {
2255                    SanityManager.ASSERT(scan_position != null);
2256                }
2257
2258                if (scan_position.current_positionKey == null)
2259                {
2260                    // save position of scan by key rather than location so
2261
// that we can recover if the page with the position
2262
// disappears while we don't have a scan lock.
2263

2264                    savePosition();
2265                }
2266                this.scan_state = SCAN_HOLD_INPROGRESS;
2267            }
2268            else if (this.scan_state == SCAN_INIT)
2269            {
2270                this.scan_state = SCAN_HOLD_INIT;
2271            }
2272
2273            super.close();
2274
2275            return(false);
2276        }
2277    }
2278
2279    /**
2280     * Do work necessary to maintain the current position in the scan.
2281     * <p>
2282     * Save the current position of the scan as a key.
2283     * Do whatever is necessary to maintain the current position of the scan.
2284     * For some conglomerates this may be a no-op.
2285     *
2286     * <p>
2287     * @exception StandardException Standard exception policy.
2288     **/

2289    private void savePosition()
2290        throws StandardException
2291    {
2292        if (this.scan_state == SCAN_INPROGRESS)
2293        {
2294            // Either current_rh or positionKey is valid - the other is null.
2295
if (SanityManager.DEBUG)
2296            {
2297                SanityManager.ASSERT(
2298                    (scan_position.current_rh == null) ==
2299                    (scan_position.current_positionKey != null));
2300            }
2301
2302            try
2303            {
2304                if (scan_position.current_rh != null)
2305                {
2306                    // if scan position is not saved by key, then make it so.
2307

2308                    // must reposition to get the page latched.
2309

2310                    if (reposition(scan_position, false))
2311                    {
2312                        scan_position.current_positionKey =
2313                            runtime_mem.get_row_for_export();
2314
2315
2316                        Page page = scan_position.current_leaf.getPage();
2317
2318
2319                        RecordHandle rh =
2320                            page.fetchFromSlot(
2321                                (RecordHandle) null,
2322                                page.getSlotNumber(scan_position.current_rh),
2323                                scan_position.current_positionKey,
2324                                (FetchDescriptor) null,
2325                                true);
2326
2327                        if (SanityManager.DEBUG)
2328                        {
2329                            SanityManager.ASSERT(rh != null);
2330                        }
2331
2332                        scan_position.current_rh = null;
2333                        scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
2334
2335                        // release scan lock now that the row is saved away.
2336

2337                        if (scan_position.current_scan_pageno != 0)
2338                        {
2339                            this.getLockingPolicy().unlockScan(
2340                                scan_position.current_scan_pageno);
2341                            scan_position.current_scan_pageno = 0;
2342                        }
2343
2344                    }
2345                    else
2346                    {
2347                        // this should never happen as we hold the scan lock
2348
// on the page while maintaining the position by
2349
// recordhandle - reposition should always work in this
2350
// case.
2351

2352                        if (SanityManager.DEBUG)
2353                            SanityManager.THROWASSERT(
2354                                "Must always be able to reposition.");
2355                    }
2356                }
2357
2358            }
2359            finally
2360            {
2361
2362                if (scan_position.current_leaf != null)
2363                {
2364                    // release latch on page
2365
scan_position.current_leaf.release();
2366                    scan_position.current_leaf = null;
2367                }
2368            }
2369        }
2370
2371    }
2372
2373    /**
2374     * Do work necessary to maintain the current position in the scan.
2375     * <p>
2376     * The latched page in the conglomerate "congomid" is changing, do
2377     * whatever is necessary to maintain the current position of the scan.
2378     * For some conglomerates this may be a no-op.
2379     * <p>
2380     *
2381     * @param conglom Conglomerate object of the conglomerate being changed.
2382     * @param page Page in the conglomerate being changed.
2383     *
2384     * @exception StandardException Standard exception policy.
2385     **/

2386    public void savePosition(Conglomerate conglom, Page page)
2387        throws StandardException
2388    {
2389        // page should be latched by split. This scan is assuming that latch
2390
// and reading off it's key from the page under the split's latch.
2391
// A lock should have already been gotten on this row.
2392

2393
2394        if (SanityManager.DEBUG)
2395        {
2396            SanityManager.ASSERT(page.isLatched());
2397        }
2398
2399        /*
2400        System.out.println(
2401            "Saving position in btree at top: " +
2402            " this.conglomerate = " + this.conglomerate +
2403            " this.scan_state = " + this.scan_state);
2404        SanityManager.DEBUG_PRINT("savePosition()",
2405            "Saving position in btree at top: " +
2406            " this.conglomerate = " + this.conglomerate +
2407            " this.scan_state = " + this.scan_state);
2408        */

2409
2410
2411        if ((this.getConglomerate() == conglom) &&
2412            (this.scan_state == SCAN_INPROGRESS))
2413        {
2414            // Either current_rh or positionKey is valid - the other is null.
2415
if (SanityManager.DEBUG)
2416            {
2417                SanityManager.ASSERT(
2418                    (scan_position.current_rh == null) ==
2419                    (scan_position.current_positionKey != null));
2420            }
2421
2422            /*
2423            SanityManager.DEBUG_PRINT("savePosition()",
2424                "Saving position in btree: " +
2425                ";current_scan_pageno = " + this.current_scan_pageno +
2426                "this.current_rh = " + this.current_rh +
2427                ";page.getPageNumber() = " + page.getPageNumber() +
2428                ((this.current_rh != null) ?
2429                    (";this.current_rh.getPageNumber() = " +
2430                     this.current_rh.getPageNumber()) : ""));
2431            */

2432
2433            if (scan_position.current_rh != null &&
2434                page.getPageNumber() ==
2435                    scan_position.current_rh.getPageNumber())
2436            {
2437                scan_position.current_positionKey =
2438                    runtime_mem.get_row_for_export();
2439
2440                RecordHandle rh =
2441                    page.fetchFromSlot(
2442                        (RecordHandle) null,
2443                        page.getSlotNumber(scan_position.current_rh),
2444                        scan_position.current_positionKey,
2445                        (FetchDescriptor) null,
2446                        true);
2447
2448                if (SanityManager.DEBUG)
2449                {
2450                    SanityManager.ASSERT(rh != null);
2451                }
2452
2453                scan_position.current_rh = null;
2454                scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
2455
2456                // release the scan lock now that we have saved away the row.
2457

2458                if (scan_position.current_scan_pageno != 0)
2459                {
2460                    this.getLockingPolicy().unlockScan(
2461                        scan_position.current_scan_pageno);
2462                    scan_position.current_scan_pageno = 0;
2463                }
2464            }
2465        }
2466    }
2467
2468    public RecordHandle getCurrentRecordHandleForDebugging()
2469    {
2470        return(scan_position.current_rh);
2471    }
2472
2473    /*
2474    ** Standard toString() method. Prints out current position in scan.
2475    */

2476    public String JavaDoc toString()
2477    {
2478        if (SanityManager.DEBUG)
2479        {
2480            String JavaDoc string =
2481                "\n\tbtree = " + this.getConglomerate() +
2482                "\n\tscan direction = " +
2483                    (this instanceof BTreeForwardScan ? "forward" :
2484                    (this instanceof BTreeMaxScan ? "backward" :
2485                                                         "illegal")) +
2486                "\n\t(scan_state:" +
2487                (this.scan_state == SCAN_INIT ? "SCAN_INIT" :
2488                 this.scan_state == SCAN_INPROGRESS ? "SCAN_INPROGRESS" :
2489                 this.scan_state == SCAN_DONE ? "SCAN_DONE" :
2490                 this.scan_state == SCAN_HOLD_INIT ? "SCAN_HOLD_INIT" :
2491                 this.scan_state == SCAN_HOLD_INPROGRESS ? "SCAN_HOLD_INPROGRESS" :
2492                                                      "BAD_SCAN_STATE") +
2493                "\n\trh:" + scan_position.current_rh +
2494                "\n\tkey:" + scan_position.current_positionKey + ")" +
2495                "\n\tinit_rawtran = " + init_rawtran +
2496                "\n\tinit_hold = " + init_hold +
2497                "\n\tinit_forUpdate = " + init_forUpdate +
2498                "\n\tinit_useUpdateLocks = " + init_useUpdateLocks +
2499                "\n\tinit_scanColumnList = " + init_scanColumnList +
2500                "\n\tinit_scanColumnList.size() = "+ (
2501                    (init_scanColumnList != null ?
2502                        init_scanColumnList.size() : 0)) +
2503                "\n\tinit_template = " +
2504                    RowUtil.toString(init_template) +
2505                "\n\tinit_startKeyValue = " +
2506                    RowUtil.toString(init_startKeyValue) +
2507                "\n\tinit_startSearchOperator = " +
2508                    (init_startSearchOperator == ScanController.GE ? "GE" :
2509                    (init_startSearchOperator == ScanController.GT ? "GT" :
2510                     Integer.toString(init_startSearchOperator))) +
2511                "\n\tinit_qualifier[] = " + init_qualifier +
2512                "\n\tinit_stopKeyValue = " +
2513                    RowUtil.toString(init_stopKeyValue) +
2514                "\n\tinit_stopSearchOperator = " +
2515                    (init_stopSearchOperator == ScanController.GE ? "GE" :
2516                    (init_stopSearchOperator == ScanController.GT ? "GT" :
2517                     Integer.toString(init_stopSearchOperator))) +
2518                "\n\tstat_numpages_visited = " +
2519                    stat_numpages_visited +
2520                "\n\tstat_numrows_visited = " +
2521                    stat_numrows_visited +
2522                "\n\tstat_numrows_qualified = " +
2523                    stat_numrows_qualified +
2524                "\n\tstat_numdeleted_rows_visited = " +
2525                    stat_numdeleted_rows_visited ;
2526
2527            return(string);
2528        }
2529        else
2530        {
2531            return(null);
2532        }
2533    }
2534}
2535
Popular Tags