KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > store > raw > data > BasePage


1 /*
2
3    Derby - Class org.apache.derby.impl.store.raw.data.BasePage
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.raw.data;
23
24 import org.apache.derby.iapi.reference.SQLState;
25
26 import org.apache.derby.iapi.services.io.FormatableBitSet;
27 import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
28 import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
29
30 import org.apache.derby.iapi.services.locks.C_LockFactory;
31 import org.apache.derby.iapi.services.locks.Lockable;
32 import org.apache.derby.iapi.services.locks.Latch;
33 import org.apache.derby.iapi.services.locks.VirtualLockTable;
34
35 import org.apache.derby.iapi.services.sanity.SanityManager;
36
37 import org.apache.derby.iapi.services.io.LimitObjectInput;
38 import org.apache.derby.iapi.services.io.TypedFormat;
39
40 import org.apache.derby.iapi.error.StandardException;
41
42 import org.apache.derby.iapi.store.raw.AuxObject;
43 import org.apache.derby.iapi.store.raw.ContainerHandle;
44 import org.apache.derby.iapi.store.raw.ContainerKey;
45 import org.apache.derby.iapi.store.raw.FetchDescriptor;
46 import org.apache.derby.iapi.store.raw.Page;
47 import org.apache.derby.iapi.store.raw.PageKey;
48 import org.apache.derby.iapi.store.raw.RecordHandle;
49 import org.apache.derby.iapi.store.raw.RawStoreFactory;
50 import org.apache.derby.iapi.store.raw.xact.RawTransaction;
51 import org.apache.derby.iapi.store.raw.log.LogInstant;
52
53 import org.apache.derby.iapi.store.access.Qualifier;
54 import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
55
56 import org.apache.derby.iapi.types.DataValueDescriptor;
57
58 import java.io.IOException JavaDoc;
59 import java.io.OutputStream JavaDoc;
60 import java.io.ObjectInput JavaDoc;
61
62 import java.util.Hashtable JavaDoc;
63 import java.util.Observer JavaDoc;
64 import java.util.Observable JavaDoc;
65
66
67 /**
68
69   This class implements all the the generic locking behaviour for a Page.
70   It leaves method used to log and store the records up to sub-classes.
71   It is intended that the object can represent multiple pages from different
72   containers during its lifetime.
73   <P>
74   A page contains a set of records, which can be accessed by "slot",
75   which defines the order of the records on the page, or by "id" which
76   defines the identity of the records on the page. Clients access
77   records by both slot and id, depending on their needs.
78   <P>
79   BasePage implements Observer to watch the ContainerHandle which notifies
80   its Observers when it is closing.
81
82   <BR>
83   MT - mutable
84
85  **/

86
87
88 public abstract class BasePage implements Page, Lockable, Observer JavaDoc, TypedFormat
89 {
90
91     /**
92         auxiliary object
93
94         MT - mutable - content dynamic : single thread required. This reference is
95         set while the page is latched and returned to callers of page while the page is latched.
96         For correct MT behaviour it is assumed that the caller discards any reference to an
97         auxiliary object once the page is unlatched. The reference mya be cleared while
98         the page is latched, or while the page is being cleaned from the cache. In the latter
99         case the cache manager ensures that only a single thread can access this object.
100     */

101     private AuxObject auxObj;
102
103     /**
104         this page's identity
105         <BR>
106         MT - immutable - content dynamic : single thread required
107     */

108     protected PageKey identity;
109
110     /**
111         In-memory slot table, array of StoredRecordHeaders.
112         <BR>
113         MT - Immutable - Content Dynamic : Single thread required.
114     */

115     private StoredRecordHeader[] headers; // in memory slot table
116

117     private int recordCount;
118
119     /**
120         Page owner during exclusive access.
121
122         MT - mutable : single thread required, provided by Lockable single thread required.
123     */

124     protected BaseContainerHandle owner;
125
126     /**
127         Count of times a latch is held nested during an abort
128     */

129     private int nestedLatch;
130
131     /**
132         LockManager held latch during exclusive access.
133         When this is not null, latch.getQualifier() == owner
134     */

135     private Latch myLatch;
136     
137     protected boolean inClean; // is the page being cleaned
138

139     /**
140      * Used to determine latch state of a page.
141      *
142      * MT - mutable
143      *
144      * There are 3 latch states for a page:
145      *
146      * UNLATCHED - (owner == null)
147      * PRELATCH - (owner != null) && preLatch
148      * LATCHED - (owner != null) && !preLatch
149      *
150      * A page may be "cleaned" while it is either UNLATCHED, or PRELATCH, but
151      * it must wait for it to be not LATCHED.
152      *
153      * A page may move from UNLATCHED to PRELATCH, while being cleaned.
154      * A page must wait for !inClean before it can move from PRELATCH to
155      * LATCHED.
156      **/

157     protected boolean preLatch;
158
159     /**
160         Instant of last log record that updated this page.
161
162         <BR> MT - mutable : latched
163     */

164     private LogInstant lastLog;
165
166     /**
167         Version of the page.
168
169         <BR> MT - mutable : single thread required - The page must be latched to access
170         this variable or the page muts be in the noidentiy state.
171     */

172     private long pageVersion = 0; // version of the page
173

174     /**
175         Status of the page
176      */

177     private byte pageStatus;
178
179     /**
180         Values for pageStatus flag
181
182         page goes thru the following transition:
183         VALID_PAGE <-> deallocated page -> free page <-> VALID_PAGE
184
185         deallocated and free page are both INVALID_PAGE as far as BasePage is concerned.
186         When a page is deallocated, it transitioned from VALID to INVALID.
187         When a page is allocated, it trnasitioned from INVALID to VALID.
188
189     */

190     public static final byte VALID_PAGE = 1;
191     public static final byte INVALID_PAGE = 2;
192
193     /**
194         Init page flag.
195
196         INIT_PAGE_REUSE - set if page is being initialized for reuse
197         INIT_PAGE_OVERFLOW - set if page will be an overflow page
198         INIT_PAGE_REUSE_RECORDID - set if page is being reused and its record
199                         id can be reset to RecordHandle.FIRST_RECORD_ID, rather
200                         to 1+ next recordId on the page
201     */

202     public static final int INIT_PAGE_REUSE = 0x1;
203     public static final int INIT_PAGE_OVERFLOW = 0x2;
204     public static final int INIT_PAGE_REUSE_RECORDID = 0x4;
205
206     /**
207         Log Record flag. Why the before image of this record is being logged
208
209         LOG_RECORD_FOR_UPDATE - set if the record is being logged for update.
210         LOG_RECORD_DEFAULT - for non update.
211         LOG_RECORD_FOR_PURGE - set if the record is being logged for purges
212                                and no data required to ve logged.
213         The other cases (copy, purge, delete), we don't need to distinguish,
214         leave no bit set.
215      */

216     public static final int LOG_RECORD_DEFAULT = 0x0;
217     public static final int LOG_RECORD_FOR_UPDATE = 0x1;
218     public static final int LOG_RECORD_FOR_PURGE = 0x2;
219
220     /**
221      ** Create a new, empty page.
222      **/

223     
224     protected BasePage()
225     {
226         
227     }
228
229     /**
230         Initialized the BasePage.
231         <p>
232         Initialize the object, ie. perform work normally perfomed in
233         constructor. Called by setIdentity() and createIdentity().
234     */

235     protected void initialize()
236     {
237         setAuxObject(null);
238         identity = null;
239         recordCount = 0;
240         clearLastLogInstant();
241
242         if (SanityManager.DEBUG)
243         {
244             if (nestedLatch != 0)
245                 SanityManager.THROWASSERT("nestedLatch is non-zero in initialize - value = " + nestedLatch);
246             if (inClean)
247                 SanityManager.THROWASSERT("inClean is true in initialize");
248             if (preLatch)
249                 SanityManager.THROWASSERT("preLatch is true in initialize");
250         }
251
252     }
253
254     /**
255         Must be called by a sub-class before calling setHeaderAtSlot.
256
257     */

258     protected void initializeHeaders(int numRecords)
259     {
260
261         if (SanityManager.DEBUG)
262         {
263             if (recordCount != 0)
264                 SanityManager.THROWASSERT(
265                         "record count = " + recordCount +
266                         " before initSlotTable is called");
267         }
268
269         headers = new StoredRecordHeader[numRecords];
270     }
271
272
273     /*
274     ** Cacheable methods
275     */

276
277     protected void fillInIdentity(PageKey key) {
278         if (SanityManager.DEBUG) {
279             SanityManager.ASSERT(identity == null);
280         }
281
282         identity = key;
283     }
284
285     public void clearIdentity() {
286
287         if (SanityManager.DEBUG) {
288             SanityManager.ASSERT(!isLatched());
289         }
290
291         identity = null;
292
293         cleanPageForReuse();
294     }
295
296
297     /**
298         Initialized this page for reuse or first use
299     */

300     protected void cleanPageForReuse()
301     {
302         setAuxObject(null);
303         recordCount = 0;
304     }
305
306
307     /**
308         OK to hand object outside to cache..
309     */

310     public Object JavaDoc getIdentity() {
311         return identity;
312     }
313
314     /*
315     ** Methods of Page
316     */

317
318     private static final RecordHandle InvalidRecordHandle =
319         new RecordId(
320             new PageKey(
321                 new ContainerKey(0,0), ContainerHandle.INVALID_PAGE_NUMBER),
322                 RecordHandle.INVALID_RECORD_HANDLE);
323
324     public final RecordHandle getInvalidRecordHandle()
325     {
326         // a static invalid record handle
327
return InvalidRecordHandle;
328     }
329
330     public static final RecordHandle MakeRecordHandle(PageKey pkey, int recordHandleConstant)
331          throws StandardException
332     {
333         if (recordHandleConstant >= RecordHandle.FIRST_RECORD_ID)
334         {
335             throw StandardException.newException(
336                 SQLState.DATA_CANNOT_MAKE_RECORD_HANDLE,
337                 new Long JavaDoc(recordHandleConstant));
338         }
339
340         return new RecordId(pkey, recordHandleConstant);
341     }
342
343     public final RecordHandle makeRecordHandle(int recordHandleConstant)
344          throws StandardException
345     {
346         return MakeRecordHandle(getPageId(), recordHandleConstant);
347     }
348
349     /** @see Page#getPageNumber */
350     public final long getPageNumber() {
351         if (SanityManager.DEBUG) {
352             SanityManager.ASSERT(isLatched(), "page is not latched.");
353             SanityManager.ASSERT(identity != null, "identity is null.");
354         }
355
356         return identity.getPageNumber();
357     }
358
359     public final RecordHandle getRecordHandle(int recordId) {
360         if (SanityManager.DEBUG) {
361             SanityManager.ASSERT(isLatched());
362         }
363
364         int slot = findRecordById(recordId, FIRST_SLOT_NUMBER);
365         if (slot < 0)
366             return null;
367
368         return getRecordHandleAtSlot(slot);
369     }
370
371     public final RecordHandle getRecordHandleAtSlot(int slot) {
372         return getHeaderAtSlot(slot).getHandle(getPageId(), slot);
373     }
374
375     /**
376       @see Page#recordExists
377       @exception StandardException recordHandle is not a valid record handle
378     */

379     public final boolean recordExists(RecordHandle handle, boolean ignoreDelete)
380          throws StandardException
381     {
382         if (SanityManager.DEBUG) {
383             SanityManager.ASSERT(isLatched());
384         }
385
386         if (handle.getId() < RecordHandle.FIRST_RECORD_ID)
387         {
388             throw StandardException.newException(
389                     SQLState.DATA_INVALID_RECORD_HANDLE, handle);
390         }
391
392         if (handle.getPageNumber() != getPageNumber())
393             return false;
394
395         int slot = findRecordById(handle.getId(), handle.getSlotNumberHint());
396         return (slot >= FIRST_SLOT_NUMBER &&
397                 (ignoreDelete || !isDeletedAtSlot(slot)));
398     }
399
400     /**
401         <OL>
402         <LI>Lock the record (according to the locking policy)
403         <LI>If the record is deleted then return null. We must check after we hold the lock to
404         ensure that we don't look at the delete status of an uncommitted record.
405         <LI>Fetch the record
406         <LI>Unlock the record (according to the locking policy)
407         </OL>
408
409         @see Page#fetch
410
411         @exception StandardException messageId equals StandardException.newException(SQLState.RECORD_VANISHED
412         If the record identfied by handle does not exist on this page.
413
414         @exception StandardException Standard Cloudscape error policy
415         @exception StandardException record is not on page with message id equal to
416             StandardException.newException(SQLState.RECORD_VANISHED.
417     */

418
419     public RecordHandle fetch(
420     RecordHandle handle,
421     Object JavaDoc[] row,
422     FormatableBitSet validColumns,
423     boolean forUpdate)
424     throws StandardException {
425
426         if (SanityManager.DEBUG) {
427             SanityManager.ASSERT(isLatched());
428         }
429
430         owner.getLockingPolicy().lockRecordForRead(myLatch, handle, forUpdate);
431
432         // See if the record is deleted or not.
433
int slot = getSlotNumber(handle);
434
435         StoredRecordHeader recordHeader = getHeaderAtSlot(slot);
436
437         if (recordHeader.isDeleted())
438             return null;
439
440         FetchDescriptor hack_fetch =
441             new FetchDescriptor(
442                     row.length, validColumns, (Qualifier[][]) null);
443
444         // magic to copy rows across ...
445
restoreRecordFromSlot(
446             slot, row, hack_fetch, handle, recordHeader, true);
447
448         owner.getLockingPolicy().unlockRecordAfterRead(
449                 owner.getTransaction(), owner, handle, forUpdate, true);
450
451         return handle;
452     }
453
454
455     public RecordHandle fetchFromSlot(
456     RecordHandle rh,
457     int slot,
458     Object JavaDoc[] row,
459     FetchDescriptor fetchDesc,
460     boolean ignoreDelete)
461          throws StandardException
462     {
463         if (SanityManager.DEBUG) {
464             SanityManager.ASSERT(isLatched());
465
466             if (rh != null)
467                 SanityManager.ASSERT(getSlotNumber(rh) == slot);
468         }
469
470         checkSlotOnPage(slot);
471
472         StoredRecordHeader recordHeader = getHeaderAtSlot(slot);
473
474         if (rh == null)
475             rh = recordHeader.getHandle(getPageId(), slot);
476
477         if (!ignoreDelete && recordHeader.isDeleted())
478             return null;
479
480         /*
481         SanityManager.DEBUG_PRINT("fetchFromSlot", "before.");
482         SanityManager.showTrace(new Throwable());
483         SanityManager.DEBUG_PRINT("fetchFromSlot", "fetchDesc = " + fetchDesc);
484
485         if (fetchDesc != null)
486         {
487             SanityManager.DEBUG_PRINT("fetchFromSlot",
488                 ";fetchDesc.getMaxFetchColumnId() = " +
489                     fetchDesc.getMaxFetchColumnId() +
490                 ";fetchDesc.getValidColumns() = " +
491                     fetchDesc.getValidColumns() +
492                 ";fetchDesc.getQualifierList() = " +
493                     fetchDesc.getQualifierList()
494             );
495         }
496         */

497
498         return(
499             restoreRecordFromSlot(
500                 slot, row, fetchDesc, rh, recordHeader, true) ? rh : null);
501     }
502
503
504     /**
505         @exception StandardException Standard Cloudscape error policy
506         @see Page#fetchFieldFromSlot
507      */

508     public final RecordHandle fetchFieldFromSlot(
509     int slot,
510     int fieldId,
511     Object JavaDoc column)
512         throws StandardException
513     {
514         // need to allocate row with fieldId cols because of sparse row change
515
// needs to be RESOLVED
516
Object JavaDoc[] row = new Object JavaDoc[fieldId + 1];
517         row[fieldId] = column;
518         FormatableBitSet singleColumn = new FormatableBitSet(fieldId + 1);
519
520         singleColumn.set(fieldId);
521
522         FetchDescriptor fetchDesc =
523             new FetchDescriptor(fieldId + 1, singleColumn,(Qualifier[][]) null);
524
525         return(fetchFromSlot(null, slot, row, fetchDesc, true));
526     }
527
528     /**
529         @exception StandardException Record does not exist on this page.
530
531         @see Page#getSlotNumber
532      */

533     public final int getSlotNumber(RecordHandle handle)
534          throws StandardException
535     {
536         if (SanityManager.DEBUG) {
537             SanityManager.ASSERT(isLatched());
538         }
539
540         int slot = findRecordById(handle.getId(), handle.getSlotNumberHint());
541
542         if (slot < 0)
543         {
544             throw StandardException.newException(
545                     SQLState.RAWSTORE_RECORD_VANISHED, handle);
546         }
547
548         return slot;
549     }
550
551     /**
552         @exception StandardException Record does not exist on this page.
553
554         @see Page#getNextSlotNumber
555      */

556     public final int getNextSlotNumber(RecordHandle handle)
557          throws StandardException
558     {
559         if (SanityManager.DEBUG) {
560             SanityManager.ASSERT(isLatched());
561         }
562
563         int slot = findNextRecordById(handle.getId());
564
565         return slot;
566     }
567
568     /** @see Page#insertAtSlot
569         @exception StandardException Standard Cloudscape error policy
570      */

571     public RecordHandle insertAtSlot(
572     int slot,
573     Object JavaDoc[] row,
574     FormatableBitSet validColumns,
575     LogicalUndo undo,
576     byte insertFlag,
577     int overflowThreshold)
578         throws StandardException
579     {
580         if (SanityManager.DEBUG) {
581             if (overflowThreshold == 0)
582                 SanityManager.THROWASSERT("overflowThreshold cannot be 0");
583         }
584
585         if ((insertFlag & Page.INSERT_DEFAULT) == Page.INSERT_DEFAULT) {
586             return (insertNoOverflow(slot, row, validColumns, undo, insertFlag, overflowThreshold));
587         } else {
588             if (SanityManager.DEBUG) {
589                 if (undo != null)
590                     SanityManager.THROWASSERT("logical undo with overflow allowed on insert " + undo.toString());
591             }
592             return (insertAllowOverflow(slot,
593                 row, validColumns, 0, insertFlag, overflowThreshold, (RecordHandle) null));
594         }
595     }
596     
597     protected RecordHandle insertNoOverflow(
598     int slot,
599     Object JavaDoc[] row,
600     FormatableBitSet validColumns,
601     LogicalUndo undo,
602     byte insertFlag,
603     int overflowThreshold)
604         throws StandardException
605     {
606         
607         if (SanityManager.DEBUG) {
608             SanityManager.ASSERT(isLatched());
609         }
610
611         if (!owner.updateOK())
612         {
613             throw StandardException.newException(
614                     SQLState.DATA_CONTAINER_READ_ONLY);
615         }
616
617         if (slot < FIRST_SLOT_NUMBER || slot > recordCount)
618         {
619             throw StandardException.newException(
620                     SQLState.DATA_SLOT_NOT_ON_PAGE);
621         }
622
623         if (!allowInsert())
624             return null;
625
626         RawTransaction t = owner.getTransaction();
627
628         // logical operations not allowed in internal transactions.
629
if (undo != null) {
630             t.checkLogicalOperationOk();
631         }
632
633         int recordId;
634         RecordHandle handle;
635
636         do {
637
638             // loop until we get a new record id we can get a lock on.
639

640             // If we can't get the lock without waiting then assume the record
641
// id is owned by another xact. The current heap overflow
642
// algorithm makes this likely, as it first try's to insert a row
643
// telling raw store to fail if it doesn't fit on the page getting
644
// a lock on an id that never makes it to disk. The inserting
645
// transaction will hold a lock on this "unused" record id until
646
// it commits. The page can leave the cache at this point, and
647
// the inserting transaction has not dirtied the page (it failed
648
// after getting the lock but before logging anything), another
649
// inserting transaction will then get the same id as the
650
// previous inserter - thus the loop on lock waits.
651
//
652
// The lock we request indicates that this is a lock for insert,
653
// which the locking policy may use to perform locking concurrency
654
// optimizations.
655

656             recordId = newRecordIdAndBump();
657             handle = new RecordId(getPageId(), recordId, slot);
658             
659         } while(!owner.getLockingPolicy().lockRecordForWrite(
660                     t, handle,
661                     true /* lock is for insert */,
662                     false /* don't wait for grant */));
663
664
665         owner.getActionSet().actionInsert(t, this, slot, recordId, row, validColumns,
666             undo, insertFlag, 0, false, -1, (DynamicByteArrayOutputStream) null, -1, overflowThreshold);
667
668         // at this point the insert has been logged and made on the physical
669
// page the in-memory manipulation of the slot table is also performed
670
// by the PageActions object that implements actionInsert.
671

672         return handle;
673     }
674
675     /** @see Page#insert
676         @exception StandardException Standard Cloudscape error policy
677      */

678     public final RecordHandle insert(
679     Object JavaDoc[] row,
680     FormatableBitSet validColumns,
681     byte insertFlag,
682     int overflowThreshold)
683         throws StandardException
684     {
685
686         if (SanityManager.DEBUG) {
687             if (overflowThreshold == 0)
688                 SanityManager.THROWASSERT("overflowThreshold much be greater than 0");
689         }
690
691         if (((insertFlag & Page.INSERT_DEFAULT) == Page.INSERT_DEFAULT)) {
692             return (insertAtSlot(recordCount, row, validColumns,
693                 (LogicalUndo) null, insertFlag, overflowThreshold));
694         } else {
695             return (insertAllowOverflow(recordCount, row, validColumns, 0,
696                 insertFlag, overflowThreshold, (RecordHandle) null));
697         }
698     }
699
700     /**
701         Insert a row allowing overflow.
702
703         If handle is supplied then the record at that hanlde will be updated
704         to indicate it is a partial row and it has an overflow portion.
705
706         @exception StandardException Standard Cloudscape error policy
707     */

708     public RecordHandle insertAllowOverflow(
709     int slot,
710     Object JavaDoc[] row,
711     FormatableBitSet validColumns,
712     int startColumn,
713     byte insertFlag,
714     int overflowThreshold,
715     RecordHandle nextPortionHandle)
716         throws StandardException
717     {
718
719         BasePage curPage = this;
720
721         if (!curPage.owner.updateOK())
722         {
723             throw StandardException.newException(
724                     SQLState.DATA_CONTAINER_READ_ONLY);
725         }
726
727
728         // Handle of the first portion of the chain
729
RecordHandle headHandle = null;
730         RecordHandle handleToUpdate = null;
731
732         RawTransaction t = curPage.owner.getTransaction();
733
734         for (;;) {
735
736             if (SanityManager.DEBUG) {
737                 SanityManager.ASSERT(curPage.isLatched());
738             }
739
740             if (!curPage.allowInsert())
741                 return null;
742
743             // 'this' is the head page
744
if (curPage != this)
745                 slot = curPage.recordCount;
746
747             boolean isLongColumns = false;
748             int realStartColumn = -1;
749             int realSpaceOnPage = -1;
750
751             DynamicByteArrayOutputStream logBuffer = null;
752
753             // allocate new record id and handle
754
int recordId = curPage.newRecordIdAndBump();
755             RecordHandle handle =
756                 new RecordId(curPage.getPageId(), recordId, slot);
757
758             if (curPage == this) {
759
760
761                 // Lock the row, if it is the very first portion of the record.
762
if (handleToUpdate == null) {
763
764                     while (!owner.getLockingPolicy().lockRecordForWrite(
765                                 t, handle,
766                                 true /* lock is for insert */,
767                                 false /* don't wait for grant */)) {
768
769                         // loop until we get a new record id we can get a lock
770
// on. If we can't get the lock without waiting then
771
// assume the record id is owned by another xact. The
772
// current heap overflow algorithm makes this likely,
773
// as it first try's to insert a row telling raw store
774
// to fail if it doesn't fit on the page getting a lock
775
// on an id that never makes it to disk. The
776
// inserting transaction will hold a lock on this
777
// "unused" record id until it commits. The page can
778
// leave the cache at this point, and the inserting
779
// transaction has not dirtied the page (it failed
780
// after getting the lock but before logging anything),
781
// another inserting transaction will then get the
782
// same id as the previous inserter - thus the loop on
783
// lock waits.
784
//
785
// The lock we request indicates that this is a lock
786
// for insert, which the locking policy may use to
787
// perform locking concurrency optimizations.
788

789                         // allocate new record id and handle
790
recordId = curPage.newRecordIdAndBump();
791                         handle =
792                             new RecordId(curPage.getPageId(), recordId, slot);
793                     }
794                 }
795
796                 headHandle = handle;
797             }
798
799             do {
800
801                 // do this loop at least once. If we caught a long Column,
802
// then, we redo the insert with saved logBuffer.
803
try {
804
805                     startColumn =
806                         owner.getActionSet().actionInsert(
807                             t, curPage, slot, recordId,
808                             row, validColumns, (LogicalUndo) null,
809                             insertFlag, startColumn, false,
810                             realStartColumn, logBuffer, realSpaceOnPage,
811                             overflowThreshold);
812                     isLongColumns = false;
813
814                 } catch (LongColumnException lce) {
815
816
817                     // we caught a long column exception
818
// three things should happen here:
819
// 1. insert the long column into overflow pages.
820
// 2. append the overflow field header in the main chain.
821
// 3. continue the insert in the main data chain.
822
logBuffer = new DynamicByteArrayOutputStream(lce.getLogBuffer());
823
824                     // step 1: insert the long column ... use the same
825
// insertFlag as the rest of the row.
826
RecordHandle longColumnHandle =
827                         insertLongColumn(curPage, lce, insertFlag);
828
829                     // step 2: append the overflow field header to the log buffer
830
int overflowFieldLen = 0;
831                     try {
832                         overflowFieldLen +=
833                             appendOverflowFieldHeader((DynamicByteArrayOutputStream)logBuffer, longColumnHandle);
834                     } catch (IOException JavaDoc ioe) {
835                         // YYZ: revisit... ioexception, insert failed...
836
return null;
837                     }
838
839                     // step 3: continue the insert in the main data chain
840
// need to pass the log buffer, and start column to the next insert.
841
realStartColumn = lce.getNextColumn() + 1;
842                     realSpaceOnPage = lce.getRealSpaceOnPage() - overflowFieldLen;
843
844                     isLongColumns = true;
845                 }
846             } while (isLongColumns);
847
848             if (handleToUpdate != null) {
849                 // update the recordheader on the previous page
850
updateOverflowDetails(handleToUpdate, handle);
851             }
852
853             // all done
854
if (startColumn == -1) {
855
856                 if (curPage != this)
857                     curPage.unlatch();
858
859                 if (nextPortionHandle != null) {
860                     // need to update the overflow details of the last portion
861
// to point to the existing portion
862
updateOverflowDetails(handle, nextPortionHandle);
863                 }
864
865                 return headHandle;
866             }
867
868             handleToUpdate = handle;
869                 
870             BasePage nextPage =
871                 curPage.getOverflowPageForInsert(
872                     slot, row, validColumns,startColumn);
873
874             if (curPage != this)
875                 curPage.unlatch();
876             curPage = nextPage;
877         }
878
879     }
880
881     /**
882       
883         When we update a column, it turned into a long column. Need to change
884         the update to effectively insert a new long column chain.
885
886         @exception StandardException Unexpected exception from the implementation
887      */

888     protected RecordHandle insertLongColumn(BasePage mainChainPage,
889             LongColumnException lce, byte insertFlag)
890         throws StandardException
891     {
892
893         // Object[] row = new Object[1];
894
// row[0] = (Object) lce.getColumn();
895
Object JavaDoc[] row = new Object JavaDoc[1];
896         row[0] = lce.getColumn();
897
898         RecordHandle firstHandle = null;
899         RecordHandle handle = null;
900         RecordHandle prevHandle = null;
901         BasePage curPage = mainChainPage;
902         BasePage prevPage = null;
903         boolean isFirstPage = true;
904
905         // when inserting a long column startCOlumn is just used
906
// as a flag. -1 means the insert is complete, != -1 indicates
907
// more inserts are required.
908
int startColumn = 0;
909         RawTransaction t = curPage.owner.getTransaction();
910
911         do {
912             // in this loop, we do 3 things:
913
// 1. get a new overflow page
914
// 2. insert portion of a long column
915
// 3. update previous handle, release latch on previous page
916

917             if (!isFirstPage) {
918                 prevPage = curPage;
919                 prevHandle = handle;
920             }
921
922             // step 1. get a new overflow page
923
curPage = (BasePage) getNewOverflowPage();
924
925             if (SanityManager.DEBUG) {
926                 SanityManager.ASSERT(curPage.isLatched());
927                 SanityManager.ASSERT(curPage.allowInsert());
928             }
929
930             int slot = curPage.recordCount;
931
932             int recordId = curPage.newRecordId();
933             handle = new RecordId(curPage.getPageId(), recordId, slot);
934
935             if (isFirstPage)
936                 firstHandle = handle;
937
938             // step 2: insert column portion
939
startColumn = owner.getActionSet().actionInsert(t, curPage, slot, recordId,
940                 row, (FormatableBitSet)null, (LogicalUndo) null, insertFlag,
941                 startColumn, true, -1, (DynamicByteArrayOutputStream) null, -1, 100);
942
943             // step 3: if it is not the first page, update previous page,
944
// then release latch on prevPage
945
if (!isFirstPage) {
946                 // for the previous page, add an overflow field header,
947
// and update the record header to show 2 fields
948
prevPage.updateFieldOverflowDetails(prevHandle, handle);
949                 prevPage.unlatch();
950                 prevPage = null;
951             } else
952                 isFirstPage = false;
953
954         } while (startColumn != (-1)) ;
955
956         if (curPage != null) {
957             curPage.unlatch();
958             curPage = null;
959         }
960
961         return (firstHandle);
962     }
963
964
965     /**
966         The page or its header is about to be modified.
967         Loggable actions use this to make sure the page gets cleaned if a
968         checkpoint is taken after any log record is sent to the log stream but
969         before the page is actually dirtied.
970     */

971     public abstract void preDirty();
972
973     /**
974         Update the overflow pointer for a long row
975
976         <BR> MT - latched - page latch must be held
977
978         @param handle handle of the record for long row
979         @param overflowHandle the overflow (continuation) pointer for the long row
980
981         @exception StandardException Standard Cloudscape error policy
982     */

983     public abstract void updateOverflowDetails(RecordHandle handle, RecordHandle overflowHandle)
984         throws StandardException;
985
986     /**
987         Update the overflow pointer for a long column
988
989         <BR> MT - latched - page latch must be held
990
991         @param handle handle of the record for long row
992         @param overflowHandle the overflow (continuation) pointer for the long row
993
994         @exception StandardException Standard Cloudscape error policy
995     */

996     public abstract void updateFieldOverflowDetails(RecordHandle handle, RecordHandle overflowHandle)
997         throws StandardException;
998
999     /**
1000        Append an overflow pointer to a partly logged row,
1001        to point to a long column that just been logged.
1002
1003        <BR> MT - latched - page latch must be held
1004
1005        @param logBuffer The buffer that contains the partially logged row.
1006        @param overflowHandle the overflow (continuation) pointer
1007                                to the beginning of the long column
1008
1009        @exception StandardException Standard Cloudscape error policy
1010    */

1011    public abstract int appendOverflowFieldHeader(DynamicByteArrayOutputStream logBuffer, RecordHandle overflowHandle)
1012        throws StandardException, IOException JavaDoc;
1013
1014    public abstract BasePage getOverflowPageForInsert(
1015    int slot,
1016    Object JavaDoc[] row,
1017    FormatableBitSet validColumns,
1018    int startColumn)
1019        throws StandardException;
1020
1021    protected abstract BasePage getNewOverflowPage()
1022        throws StandardException;
1023
1024    public final boolean update(
1025    RecordHandle handle,
1026    Object JavaDoc[] row,
1027    FormatableBitSet validColumns)
1028        throws StandardException
1029    {
1030        if (SanityManager.DEBUG) {
1031            SanityManager.ASSERT(isLatched());
1032        }
1033
1034        if (!owner.updateOK())
1035        {
1036            throw StandardException.newException(
1037                    SQLState.DATA_CONTAINER_READ_ONLY);
1038        }
1039
1040        RawTransaction t = owner.getTransaction();
1041
1042        owner.getLockingPolicy().lockRecordForWrite(myLatch, handle);
1043
1044        int slot = getSlotNumber(handle);
1045
1046        if (isDeletedAtSlot(slot))
1047            return false;
1048
1049        doUpdateAtSlot(t, slot, handle.getId(), row, validColumns);
1050        
1051        return true;
1052    }
1053
1054
1055    /** @see Page#delete
1056        @see BasePage#deleteAtSlot
1057        @exception StandardException Standard exception policy.
1058    */

1059    public boolean delete(RecordHandle handle, LogicalUndo undo)
1060        throws StandardException
1061    {
1062        if (SanityManager.DEBUG) {
1063            SanityManager.ASSERT(isLatched());
1064        }
1065
1066        owner.getLockingPolicy().lockRecordForWrite(myLatch, handle);
1067
1068        int slot = getSlotNumber(handle);
1069
1070        if (isDeletedAtSlot(slot))
1071            return false;
1072
1073        deleteAtSlot(slot, true, undo);
1074        return true;
1075    }
1076
1077    /** @see Page#updateAtSlot
1078        @exception StandardException Standard Cloudscape error policy
1079        @exception StandardException StandardException.newException(SQLState.UPDATE_DELETED_RECORD
1080        if the record is already deleted
1081        @exception StandardException StandardException.newException(SQLState.CONTAINER_READ_ONLY
1082        if the container is read only
1083     */

1084    public final RecordHandle updateAtSlot(
1085    int slot,
1086    Object JavaDoc[] row,
1087    FormatableBitSet validColumns)
1088         throws StandardException
1089    {
1090        if (SanityManager.DEBUG) {
1091            SanityManager.ASSERT(isLatched());
1092        }
1093
1094        if (!owner.updateOK())
1095        {
1096            throw StandardException.newException(
1097                    SQLState.DATA_CONTAINER_READ_ONLY);
1098        }
1099
1100        if (isDeletedAtSlot(slot))
1101        {
1102            throw StandardException.newException(
1103                    SQLState.DATA_UPDATE_DELETED_RECORD);
1104        }
1105
1106
1107        RecordHandle handle = getRecordHandleAtSlot(slot);
1108
1109        RawTransaction t = owner.getTransaction();
1110
1111        doUpdateAtSlot(t, slot, handle.getId(), row, validColumns);
1112        
1113        return handle;
1114    }
1115
1116    public abstract void doUpdateAtSlot(
1117    RawTransaction t,
1118    int slot,
1119    int id,
1120    Object JavaDoc[] row,
1121    FormatableBitSet validColumns)
1122        throws StandardException;
1123
1124    /** @see Page#updateFieldAtSlot
1125        @exception StandardException Standard Cloudscape error policy
1126        @exception StandardException StandardException.newException(SQLState.UPDATE_DELETED_RECORD
1127        if the record is already deleted
1128        @exception StandardException StandardException.newException(SQLState.CONTAINER_READ_ONLY
1129        if the container is read only
1130    */

1131    public RecordHandle updateFieldAtSlot(
1132    int slot,
1133    int fieldId,
1134    Object JavaDoc newValue,
1135    LogicalUndo undo)
1136        throws StandardException
1137    {
1138        if (SanityManager.DEBUG) {
1139            SanityManager.ASSERT(isLatched());
1140            SanityManager.ASSERT(newValue != null);
1141        }
1142
1143        if (!owner.updateOK())
1144        {
1145            throw StandardException.newException(
1146                    SQLState.DATA_CONTAINER_READ_ONLY);
1147        }
1148
1149        if (isDeletedAtSlot(slot))
1150        {
1151            throw StandardException.newException(
1152                    SQLState.DATA_UPDATE_DELETED_RECORD);
1153        }
1154
1155        RawTransaction t = owner.getTransaction();
1156        RecordHandle handle = getRecordHandleAtSlot(slot);
1157
1158        owner.getActionSet().actionUpdateField(t, this, slot,
1159                handle.getId(), fieldId, newValue, undo);
1160
1161        return handle;
1162    }
1163
1164    /** @see Page#fetchNumFields
1165        @exception StandardException Standard exception policy.
1166    */

1167    public final int fetchNumFields(RecordHandle handle)
1168         throws StandardException
1169    {
1170        if (SanityManager.DEBUG) {
1171            SanityManager.ASSERT(isLatched());
1172        }
1173
1174        return fetchNumFieldsAtSlot(getSlotNumber(handle));
1175    }
1176
1177    /** @see Page#fetchNumFieldsAtSlot
1178        @exception StandardException Standard exception policy.
1179    */

1180    public int fetchNumFieldsAtSlot(int slot)
1181         throws StandardException
1182    {
1183        if (SanityManager.DEBUG) {
1184            SanityManager.ASSERT(isLatched());
1185        }
1186
1187        return getHeaderAtSlot(slot).getNumberFields();
1188    }
1189
1190    /**
1191        @see Page#deleteAtSlot
1192
1193        @param slot the slot number
1194        @param delete true if this record is to be deleted, false if this
1195                        deleted record is to be marked undeleted
1196        @param undo logical undo logic if necessary
1197
1198        @exception StandardException Standard exception policy.
1199        @exception StandardException StandardException.newException(SQLState.UPDATE_DELETED_RECORD
1200        if an attempt to delete a record that is already deleted
1201        @exception StandardException StandardException.newException(SQLState.UNDELETE_RECORD
1202        if an attempt to undelete a record that is not deleted
1203     */

1204    public RecordHandle deleteAtSlot(int slot, boolean delete, LogicalUndo undo)
1205        throws StandardException
1206    {
1207        if (SanityManager.DEBUG) {
1208            SanityManager.ASSERT(isLatched());
1209        }
1210
1211        if (!owner.updateOK())
1212        {
1213            throw StandardException.newException(
1214                    SQLState.DATA_CONTAINER_READ_ONLY);
1215        }
1216
1217        if (delete)
1218        {
1219            if (isDeletedAtSlot(slot))
1220            {
1221                throw StandardException.newException(
1222                        SQLState.DATA_UPDATE_DELETED_RECORD);
1223            }
1224
1225        }
1226        else // undelete a deleted record
1227
{
1228            if (!isDeletedAtSlot(slot))
1229            {
1230                throw StandardException.newException(
1231                        SQLState.DATA_UNDELETE_RECORD);
1232            }
1233        }
1234
1235        RawTransaction t = owner.getTransaction();
1236
1237        // logical operations not allowed in internal transactions.
1238
if (undo != null) {
1239            t.checkLogicalOperationOk();
1240        }
1241
1242        RecordHandle handle = getRecordHandleAtSlot(slot);
1243
1244        owner.getActionSet().actionDelete(t, this, slot, handle.getId(), delete, undo);
1245        
1246        // delete/undelete the record in the stored version
1247
// and in the in memory version performed by the PageActions item
1248

1249        return handle;
1250    }
1251
1252
1253    /**
1254        Purge one or more rows on a non-overflow page.
1255
1256        @see Page#purgeAtSlot
1257        @exception StandardException Standard exception policy.
1258     */

1259    public void purgeAtSlot(int slot, int numpurges, boolean needDataLogged)
1260        throws StandardException
1261    {
1262        if (SanityManager.DEBUG)
1263        {
1264            SanityManager.ASSERT(isLatched());
1265
1266            if (isOverflowPage())
1267                SanityManager.THROWASSERT(
1268                    "purge committed deletes on an overflow page. Page = " +
1269                    this);
1270        }
1271
1272
1273        if (numpurges <= 0)
1274            return;
1275
1276        if (!owner.updateOK())
1277        {
1278            throw StandardException.newException(
1279                    SQLState.DATA_CONTAINER_READ_ONLY);
1280        }
1281
1282        if ((slot < 0) || ((slot+numpurges) > recordCount))
1283        {
1284
1285            throw StandardException.newException(
1286                    SQLState.DATA_SLOT_NOT_ON_PAGE);
1287        }
1288
1289        RawTransaction t = owner.getTransaction();
1290
1291        // lock the records to be purged
1292
int[] recordIds = new int[numpurges];
1293
1294        PageKey pageId = getPageId(); // RESOLVE: MT problem ?
1295

1296        for (int i = 0; i < numpurges; i++)
1297        {
1298            recordIds[i] = getHeaderAtSlot(slot + i).getId();
1299
1300            // get row lock on head row piece
1301
RecordHandle handle = getRecordHandleAtSlot(slot);
1302            owner.getLockingPolicy().lockRecordForWrite(t, handle, false, true);
1303
1304            // Before we purge these rows, we need to make sure they don't have
1305
// overflow rows and columns. Only clean up long rows and long
1306
// columns if this is not a temporary container, otherwise, just
1307
// loose the space.
1308

1309            if (owner.isTemporaryContainer() || entireRecordOnPage(slot+i))
1310                continue;
1311
1312            // row[slot+i] has overflow rows and/or long columns, reclaim
1313
// them in a loop.
1314
RecordHandle headRowHandle =
1315                getHeaderAtSlot(slot+i).getHandle(pageId, slot+i);
1316            purgeRowPieces(t, slot+i, headRowHandle, needDataLogged);
1317        }
1318
1319        owner.getActionSet().actionPurge(t, this, slot, numpurges, recordIds, needDataLogged);
1320
1321    }
1322
1323    /**
1324        Purge all the overflow columns and overflow rows of the record at slot.
1325        @exception StandardException Standard exception policy.
1326     */

1327    protected abstract void purgeRowPieces(RawTransaction t, int slot,
1328                                           RecordHandle headRowHandle,
1329                                           boolean needDataLogged)
1330         throws StandardException;
1331
1332
1333    /** @see Page#copyAndPurge
1334        @exception StandardException Standard exception policy.
1335    */

1336    public void copyAndPurge(Page destPage, int src_slot, int num_rows, int dest_slot)
1337         throws StandardException
1338    {
1339        if (SanityManager.DEBUG) {
1340            SanityManager.ASSERT(isLatched());
1341        }
1342
1343        if (num_rows <= 0)
1344        {
1345            throw StandardException.newException(SQLState.DATA_NO_ROW_COPIED);
1346        }
1347
1348        if (!owner.updateOK())
1349        {
1350            throw StandardException.newException(
1351                    SQLState.DATA_CONTAINER_READ_ONLY);
1352        }
1353
1354        if ((src_slot < 0) || ((src_slot + num_rows) > recordCount))
1355        {
1356            throw StandardException.newException(
1357                    SQLState.DATA_SLOT_NOT_ON_PAGE);
1358        }
1359
1360        if (SanityManager.DEBUG) {
1361            // first copy into the destination page, let it do the work
1362
// if no problem, then purge from this page
1363
SanityManager.ASSERT((destPage instanceof BasePage), "must copy from BasePage to BasePage");
1364        }
1365
1366        BasePage dpage = (BasePage)destPage;
1367
1368        // make sure they are from the same container - this means they are of
1369
// the same size and have the same page and record format.
1370

1371        PageKey pageId = getPageId(); // RESOLVE: MT problem ?
1372

1373        if (!pageId.getContainerId().equals(dpage.getPageId().getContainerId()))
1374        {
1375            throw StandardException.newException(
1376                    SQLState.DATA_DIFFERENT_CONTAINER,
1377                    pageId.getContainerId(),
1378                    dpage.getPageId().getContainerId());
1379        }
1380
1381        int[] recordIds = new int[num_rows];
1382
1383        RawTransaction t = owner.getTransaction();
1384
1385        // lock the records to be purged and calculate total space needed
1386
for (int i = 0; i < num_rows; i++)
1387        {
1388            RecordHandle handle = getRecordHandleAtSlot(src_slot+i);
1389            owner.getLockingPolicy().lockRecordForWrite(t, handle, false, true);
1390
1391            recordIds[i] = getHeaderAtSlot(src_slot + i).getId();
1392        }
1393
1394        // first copy num_rows into destination page
1395
dpage.copyInto(this, src_slot, num_rows, dest_slot);
1396
1397        // Now purge num_rows from this page
1398
// Do NOT purge overflow rows, if it has such a thing. This operation
1399
// is called by split and if the key has overflow, spliting the head
1400
// page does not copy over the remaining pieces, i.e.,the new head page
1401
// still points to those pieces.
1402

1403        owner.getActionSet().actionPurge(
1404            t, this, src_slot, num_rows, recordIds, true);
1405    }
1406
1407
1408    /**
1409        Unlatch the page.
1410        @see Page#unlatch
1411    */

1412    public void unlatch() {
1413        if (SanityManager.DEBUG) {
1414            SanityManager.ASSERT(isLatched());
1415        }
1416
1417       releaseExclusive();
1418    }
1419
1420    /** @see Page#isLatched */
1421    public boolean isLatched() {
1422        if (SanityManager.DEBUG) {
1423
1424            synchronized(this)
1425            {
1426                SanityManager.ASSERT(identity != null);
1427                if (owner != null) {
1428                    if (owner != myLatch.getQualifier())
1429                        SanityManager.THROWASSERT("Page incorrectly latched - " + owner + " " + myLatch.getQualifier());
1430                }
1431            }
1432        }
1433
1434        return owner != null;
1435    }
1436
1437    /** @see Page#recordCount */
1438    public final int recordCount() {
1439        if (SanityManager.DEBUG) {
1440            SanityManager.ASSERT(isLatched());
1441        }
1442
1443        return recordCount;
1444    }
1445
1446    /**
1447        get record count without checking for latch
1448    */

1449    protected abstract int internalDeletedRecordCount();
1450
1451    /**
1452        get record count without checking for latch
1453    */

1454    protected int internalNonDeletedRecordCount()
1455    {
1456        // deallocated or freed page, don't count
1457
if (pageStatus != VALID_PAGE)
1458            return 0;
1459
1460        int deletedCount = internalDeletedRecordCount();
1461
1462        if (deletedCount == -1) {
1463            int count = 0;
1464            int maxSlot = recordCount;
1465            for (int slot = FIRST_SLOT_NUMBER ; slot < maxSlot; slot++) {
1466                if (!isDeletedOnPage(slot))
1467                    count++;
1468            }
1469            return count;
1470
1471        } else {
1472
1473            if (SanityManager.DEBUG) {
1474                int delCount = 0;
1475                int maxSlot = recordCount;
1476                for (int slot = FIRST_SLOT_NUMBER ; slot < maxSlot; slot++) {
1477                    if (recordHeaderOnDemand(slot).isDeleted())
1478                        delCount++;
1479                }
1480                if (delCount != deletedCount)
1481                    SanityManager.THROWASSERT("incorrect deleted row count. Should be: "
1482                        + delCount + ", instead got: " + deletedCount
1483                        + ", maxSlot = " + maxSlot + ", recordCount = " + recordCount);
1484            }
1485
1486            return (recordCount - deletedCount);
1487        }
1488    }
1489
1490    /** @see Page#nonDeletedRecordCount */
1491    public int nonDeletedRecordCount() {
1492        if (SanityManager.DEBUG) {
1493            SanityManager.ASSERT(isLatched());
1494        }
1495
1496        return internalNonDeletedRecordCount();
1497
1498    }
1499
1500    /**
1501     * Is this page/deleted row a candidate for immediate reclaim space.
1502     * <p>
1503     * Used by access methods after executing a delete on "slot_just_deleted"
1504     * to ask whether a post commit should be queued to try to reclaim space
1505     * after the delete commits.
1506     * <p>
1507     * Will return true if the number of non-deleted rows on the page is
1508     * <= "num_non_deleted_rows". For instance 0 means schedule reclaim
1509     * only if all rows are deleted, 1 if all rows but one are deleted.
1510     * <p>
1511     * Will return true if the row just deleted is either a long row or long
1512     * column. In this case doing a reclaim space on the single row may
1513     * reclaim multiple pages of free space, so better to do it now rather
1514     * than wait for all rows on page to be deleted. This case is to address
1515     * the worst case scenario of all rows with long columns, but very short
1516     * rows otherwise. In this case there could be 1000's of rows on the
1517     * main page with many gigabytes of data on overflow pages in deleted space
1518     * that would not be reclaimed until all rows on the page were deleted.
1519     *
1520     * @return true if a reclaim space should be scheduled post commit on this
1521     * page, false otherwise.
1522     *
1523     * @param num_non_deleted_rows threshold number of non-deleted rows to
1524     * schedule reclaim space.
1525     * @param slot_just_deleted row on page to check for long row/long column
1526     *
1527     * @exception StandardException Standard exception policy.
1528     **/

1529    public boolean shouldReclaimSpace(
1530    int num_non_deleted_rows,
1531    int slot_just_deleted)
1532        throws StandardException
1533    {
1534        if (SanityManager.DEBUG)
1535        {
1536            SanityManager.ASSERT(isLatched());
1537        }
1538
1539        boolean ret_val = false;
1540
1541        if (internalNonDeletedRecordCount() <= num_non_deleted_rows)
1542        {
1543            ret_val = true;
1544        }
1545        else
1546        {
1547            if (!entireRecordOnPage(slot_just_deleted))
1548            {
1549                ret_val = true;
1550            }
1551        }
1552
1553        return(ret_val);
1554    }
1555
1556    // no need to check for slot on page, call already checked
1557
protected final boolean isDeletedOnPage(int slot)
1558    {
1559        return getHeaderAtSlot(slot).isDeleted();
1560    }
1561
1562
1563    /** @see Page#isDeletedAtSlot
1564        @exception StandardException Standard exception policy.
1565     */

1566    public boolean isDeletedAtSlot(int slot)
1567         throws StandardException
1568    {
1569        if (SanityManager.DEBUG) {
1570            SanityManager.ASSERT(isLatched());
1571        }
1572
1573        checkSlotOnPage(slot);
1574
1575        return isDeletedOnPage(slot);
1576    }
1577
1578    /**
1579        Set the aux object.
1580
1581        <BR> MT - single thread required. Calls via the Page interface will have the
1582            page latched, thus providing single threadedness. Otherwise calls via this class
1583            are only made when the class has no-identity, thus only a single thread can see the object.
1584
1585        @see Page#setAuxObject
1586    */

1587    public void setAuxObject(AuxObject obj)
1588    {
1589        if (SanityManager.DEBUG) {
1590            SanityManager.ASSERT((identity == null) || isLatched());
1591        }
1592
1593        if (auxObj != null) {
1594            auxObj.auxObjectInvalidated();
1595        }
1596
1597        auxObj = obj;
1598    }
1599
1600    /**
1601        Get the aux object.
1602        <BR> MT - latched - It is required the caller throws away the returned reference
1603        when the page is unlatched.
1604
1605        @see Page#getAuxObject
1606    */

1607    public AuxObject getAuxObject()
1608    {
1609        if (SanityManager.DEBUG) {
1610            SanityManager.ASSERT(isLatched());
1611        }
1612
1613        return auxObj;
1614    }
1615
1616    /*
1617    ** Methods from Lockable, just require a single exclusive locker
1618    */

1619
1620    /**
1621        Latch me.
1622        <BR>
1623        MT - single thread required (methods of Lockable)
1624        @see Lockable#lockEvent
1625    */

1626    public void lockEvent(Latch lockInfo) {
1627        if (SanityManager.DEBUG) {
1628            SanityManager.ASSERT(owner == null, "Should only be called when not locked");
1629        }
1630
1631        synchronized (this) {
1632
1633            myLatch = lockInfo;
1634
1635            // Move page state from UNLATCHED to PRELATCH, setExclusiveNo*()
1636
// routines do the work of completing the latch - using the
1637
// preLatch status. This is so that
1638
// we don't have to wait for a clean() initiated I/O here while
1639
// holding the locking system monitor.
1640
(owner = (BaseContainerHandle) lockInfo.getQualifier()).addObserver(this);
1641            preLatch = true;
1642        }
1643    }
1644
1645    /**
1646        Is another request compatible, no never.
1647        <BR> MT - single thread required (methods of Lockable)
1648        @see Lockable#requestCompatible
1649    */

1650    public boolean requestCompatible(Object JavaDoc requestedQualifier, Object JavaDoc grantedQualifier) {
1651        if (SanityManager.DEBUG) {
1652            SanityManager.ASSERT(owner != null, "Should only be called when locked");
1653        }
1654
1655        return false;
1656    }
1657
1658    /**
1659        Is another request compatible, no never.
1660        <BR> MT - single thread required (methods of Lockable)
1661        @see Lockable#requestCompatible
1662    */

1663    public boolean lockerAlwaysCompatible() {
1664        if (SanityManager.DEBUG) {
1665            SanityManager.ASSERT(owner != null, "Should only be called when locked");
1666        }
1667
1668        return false;
1669    }
1670
1671    /**
1672        Unlatch me, only to be called from lock manager.
1673        <BR> MT - single thread required (methods of Lockable)
1674
1675        @see Lockable#requestCompatible
1676    */

1677    public void unlockEvent(Latch lockInfo) {
1678        if (SanityManager.DEBUG) {
1679            SanityManager.ASSERT(owner != null, "Should only be called when locked");
1680        }
1681
1682        synchronized (this) {
1683
1684            if (SanityManager.DEBUG) {
1685                if (nestedLatch != 0)
1686                    SanityManager.THROWASSERT("nestedLatch is non-zero on unlockEvent - value = " + nestedLatch);
1687            }
1688
1689            owner.deleteObserver(this);
1690            owner = null;
1691            myLatch = null;
1692            if (inClean)
1693                notifyAll();
1694        }
1695    }
1696
1697
1698    /*
1699    ** Methods of Observer.
1700    */

1701
1702    /**
1703        This object is set to observe the BaseContainerHandle it was obtained by,
1704        that handle will notify its observers when it is being closed. In that case
1705        we will release the latch on the page held by that container.
1706
1707        <BR>
1708        MT - latched
1709
1710        @see Observer#update
1711    */

1712
1713    public void update(Observable JavaDoc obj, Object JavaDoc arg) {
1714
1715        if (SanityManager.DEBUG) {
1716            SanityManager.ASSERT(isLatched());
1717            SanityManager.ASSERT(obj == owner);
1718        }
1719
1720        releaseExclusive();
1721    }
1722
1723    /*
1724    ** Implementation specific methods
1725    */

1726
1727    /**
1728        Get the Page identifer
1729
1730        <BR> MT - RESOLVE
1731    */

1732    public PageKey getPageId() {
1733        if (SanityManager.DEBUG) {
1734            SanityManager.ASSERT(identity != null);
1735        }
1736
1737        return identity;
1738    }
1739
1740    /**
1741        Get an exclusive latch on the page.
1742        <BR>
1743        MT - thread safe
1744        @exception StandardException Standard Cloudscape policy.
1745    */

1746    public void setExclusive(BaseContainerHandle requester)
1747        throws StandardException {
1748
1749        RawTransaction t = requester.getTransaction();
1750
1751        // In some cases latches are held until after a commit or an abort
1752
// (currently internal and nested top transactions.
1753
// If this is the case then during an abort a latch
1754
// request will be made for a latch that is already held.
1755
// We do not allow the latch to be obtained multiple times
1756
// because i) lock manager might assume latches are exclusive for
1757
// performance, ii) holding a page latched means that the page is
1758
// on the container handle's obervers list, if we latched it twice
1759
// then the paeg would have to be on the list twice, which is not supported
1760
// since the page has value equality. To be on the list twice reference
1761
// equality would be required, which would mean pushing a ReferenceObservable
1762
// object for every latch; iii) other unknown reasons :-)
1763
synchronized (this)
1764        {
1765            // need synchronized block because owner may be set to null in the
1766
// middle if another thread is in the process of unlatching the
1767
// page
1768
if ((owner != null) && (t == owner.getTransaction())) {
1769
1770                if (t.inAbort()) {
1771                    //
1772
nestedLatch++;
1773                    return;
1774                }
1775            }
1776            // just deadlock out ...
1777
}
1778
1779
1780        // Latch the page, owner is set through the Lockable call backs.
1781
t.getLockFactory().latchObject(
1782            t, this, requester, C_LockFactory.WAIT_FOREVER);
1783
1784        // latch granted, but cleaner may "own" the page.
1785

1786        if (SanityManager.DEBUG) {
1787            SanityManager.ASSERT(isLatched(), "page not latched");
1788        }
1789
1790        synchronized (this)
1791        {
1792            // lockEvent() will grant latch, even if cleaner "owns" the page.
1793
// Wait here unil cleaner is done. This is safe as now we own the
1794
// latch, and have yet to do anything to the in-memory data
1795
// structures.
1796
//
1797
// Previously we would wait in lockEvent, but that caused the code
1798
// to block on I/O while holding the locking system monitor.
1799

1800            while (inClean)
1801            {
1802                try
1803                {
1804                    // Expect notify from clean() routine.
1805
wait();
1806                }
1807                catch (InterruptedException JavaDoc ie)
1808                {
1809                }
1810            }
1811
1812            // no clean taking place, so safe to move to full LATCHED state.
1813
preLatch = false;
1814        }
1815
1816    }
1817
1818    /**
1819        Get an exclusive latch on the page, but only if I don't have to wait.
1820        <BR>
1821        MT - thread safe
1822    */

1823    boolean setExclusiveNoWait(BaseContainerHandle requester) throws StandardException {
1824
1825        RawTransaction t = requester.getTransaction();
1826
1827        // comment in setExclusive()
1828
synchronized (this)
1829        {
1830            if ((owner != null) && (t == owner.getTransaction())) {
1831
1832                if (t.inAbort()) {
1833                    //
1834
nestedLatch++;
1835                    return true;
1836                }
1837            }
1838            // just deadlock out ...
1839
}
1840
1841        // Latch the page, owner is set through the Lockable call backs.
1842
boolean gotLatch = t.getLockFactory().latchObject(t, this, requester, C_LockFactory.NO_WAIT);
1843        if (!gotLatch)
1844            return false;
1845
1846        synchronized (this)
1847        {
1848            // lockEvent() will grant latch, even if cleaner "owns" the page.
1849
// Wait here unil cleaner is done. This is safe as now we own the
1850
// latch, and have yet to do anything to the in-memory data
1851
// structures.
1852
//
1853
// Previously we would wait in lockEvent, but that caused the code
1854
// to block on I/O while holding the locking system monitor.
1855

1856            while (inClean)
1857            {
1858                //if (SanityManager.DEBUG)
1859
// SanityManager.DEBUG_PRINT("setExclusiveNoWait", "in while loop.");
1860

1861                try
1862                {
1863                    // Expect notify from clean() routine.
1864
wait();
1865                }
1866                catch (InterruptedException JavaDoc ie)
1867                {
1868                }
1869            }
1870
1871            // no clean taking place, so safe to move to full LATCHED state.
1872
preLatch = false;
1873        }
1874
1875        if (SanityManager.DEBUG) {
1876            SanityManager.ASSERT(isLatched(), "page not latched");
1877        }
1878
1879        return true;
1880    }
1881
1882    /**
1883        Release the exclusive latch on the page.
1884        <BR>
1885        MT - latched
1886    */

1887    protected void releaseExclusive() /* throws StandardException */ {
1888
1889        if (SanityManager.DEBUG) {
1890            if (!isLatched())
1891            {
1892                SanityManager.THROWASSERT(
1893                    "releaseExclusive failed, nestedLatch = " + nestedLatch);
1894            }
1895        }
1896
1897        if (nestedLatch > 0) {
1898            nestedLatch--;
1899            return;
1900        }
1901
1902        RawTransaction t = owner.getTransaction();
1903        t.getLockFactory().unlatch(myLatch);
1904    }
1905
1906    /*
1907    ** Manipulation of the in-memory version of the slot table.
1908    */

1909
1910    /**
1911        Must be called by any non-abstract sub-class to initialise the slot
1912        table.
1913    */

1914
1915    protected final void setHeaderAtSlot(int slot, StoredRecordHeader rh) {
1916
1917        if (slot < headers.length)
1918        {
1919            // check that array "cache" of headers is big enough.
1920
if (rh != null)
1921            {
1922                headers[slot] = rh;
1923            }
1924        }
1925        else
1926        {
1927            // need to grow the array, just allocate new array and copy.
1928
StoredRecordHeader[] new_headers = new StoredRecordHeader[slot + 1];
1929
1930            System.arraycopy(headers, 0, new_headers, 0, headers.length);
1931
1932            headers = new_headers;
1933
1934            headers[slot] = rh;
1935        }
1936    }
1937
1938    protected final void bumpRecordCount(int number) {
1939        recordCount += number;
1940    }
1941
1942    public final StoredRecordHeader getHeaderAtSlot(int slot) {
1943
1944        if (slot < headers.length)
1945        {
1946            StoredRecordHeader rh = headers[slot];
1947
1948            return((rh != null) ? rh : recordHeaderOnDemand(slot));
1949        }
1950        else
1951        {
1952            return recordHeaderOnDemand(slot);
1953        }
1954    }
1955
1956    /**
1957        Returns true if the entire record of that slot fits inside of this
1958        page. Returns false if part of the record on this slot overflows to
1959        other pages, either due to long row or long column.
1960
1961        <BR>
1962        MT - latched
1963
1964        @exception StandardException Standard Cloudscape error policy
1965     */

1966    public abstract boolean entireRecordOnPage(int slot)
1967         throws StandardException;
1968
1969
1970    public abstract StoredRecordHeader recordHeaderOnDemand(int slot);
1971
1972    /**
1973        Is the given slot number on the page?
1974
1975        <BR>
1976        MT - latched
1977    */

1978    private final void checkSlotOnPage(int slot)
1979        throws StandardException {
1980
1981        if (SanityManager.DEBUG) {
1982            SanityManager.ASSERT(isLatched());
1983        }
1984
1985        if (slot >= FIRST_SLOT_NUMBER && slot < recordCount)
1986        {
1987                return;
1988        }
1989
1990        throw StandardException.newException(SQLState.DATA_SLOT_NOT_ON_PAGE);
1991    }
1992
1993    /**
1994        Mark the record at the passed in slot as deleted.
1995
1996        return code comes from StoredRecordHeader class:
1997            return 1, if delete status from not deleted to deleted
1998            return -1, if delete status from deleted to not deleted
1999            return 0, if status unchanged.
2000        <BR>
2001        <B>Any sub-class must call this method when deleting a record.</B>
2002
2003        <BR>
2004        MT - latched
2005
2006        @exception StandardException Standard Cloudscape error policy
2007        @exception IOException IO error accessing page
2008    */

2009    public int setDeleteStatus(int slot, boolean delete) throws StandardException, IOException JavaDoc {
2010
2011        if (SanityManager.DEBUG) {
2012            // latch check performed in checkSlotOnPage
2013
checkSlotOnPage(slot);;
2014        }
2015
2016        return (getHeaderAtSlot(slot).setDeleted(delete));
2017    }
2018
2019    /**
2020        Mark this page as being deallocated
2021
2022        @exception StandardException Cloudscape Standard error policy
2023    */

2024    public void deallocatePage() throws StandardException
2025    {
2026        if (SanityManager.DEBUG) {
2027            SanityManager.ASSERT(isLatched());
2028        }
2029
2030        if (!owner.updateOK())
2031        {
2032            throw StandardException.newException(
2033                    SQLState.DATA_CONTAINER_READ_ONLY);
2034        }
2035
2036        RawTransaction t = owner.getTransaction();
2037
2038        owner.getActionSet().actionInvalidatePage(t, this);
2039    }
2040
2041    /**
2042        Mark this page as being allocated and initialize it to a pristine page
2043        @exception StandardException Cloudscape Standard error policy
2044    */

2045    public void initPage(int initFlag, long pageOffset)
2046         throws StandardException
2047    {
2048        if (SanityManager.DEBUG) {
2049            SanityManager.ASSERT(isLatched());
2050        }
2051
2052        if (!owner.updateOK())
2053        {
2054            throw StandardException.newException(
2055                    SQLState.DATA_CONTAINER_READ_ONLY);
2056        }
2057
2058        RawTransaction t = owner.getTransaction();
2059
2060        owner.getActionSet().actionInitPage(
2061            t, this, initFlag, getTypeFormatId(), pageOffset);
2062    }
2063
2064    /**
2065        Find the slot for the record with the passed in identifier.
2066
2067        <BR>
2068        This method returns the record regardless of its deleted status.
2069        <BR>
2070        The "slotHint" argument is a hint about what slot the record id might
2071        be in. Callers may save the last slot where the record was across
2072        latch/unlatches to the page, and then pass that slot back as a hint -
2073        if the page has not shuffled slots since the last reference then the
2074        hint will succeed and a linear search is saved. If the caller has
2075        no idea where it may be, then FIRST_SLOT_NUMBER is passed in and a
2076        linear search is performed.
2077        <BR>
2078        MT - latched
2079
2080        @param recordId record id of the record to search for.
2081        @param slotHint "hint" about which slot the record might be in.
2082        
2083    */

2084    public int findRecordById(int recordId, int slotHint) {
2085
2086        if (SanityManager.DEBUG) {
2087            SanityManager.ASSERT(isLatched());
2088        }
2089
2090        if (slotHint == FIRST_SLOT_NUMBER)
2091            slotHint = recordId - RecordHandle.FIRST_RECORD_ID;
2092
2093        int maxSlot = recordCount();
2094
2095       if ((slotHint > FIRST_SLOT_NUMBER) &&
2096            (slotHint < maxSlot) &&
2097            (recordId == getHeaderAtSlot(slotHint).getId())) {
2098            return(slotHint);
2099        } else {
2100            for (int slot = FIRST_SLOT_NUMBER; slot < maxSlot; slot++) {
2101                if (recordId == getHeaderAtSlot(slot).getId()) {
2102                    return slot;
2103                }
2104            }
2105        }
2106
2107        return -1;
2108    }
2109
2110    /**
2111        Find the slot for the first record on the page with an id greater than
2112        the passed in identifier.
2113
2114        <BR>
2115        Returns the slot of the first record on the page with an id greater
2116        than the one passed in. Usefulness of this functionality depends on the
2117        clients use of the raw store interfaces. If all "new" records are
2118        always inserted at the end of the page, and the raw store continues
2119        to guarantee that all record id's will be allocated in increasing order
2120        on a given page, then a page is always sorted
2121        in record id order. For instance current heap tables function this
2122        way. If the client ever inserts at a particular slot number, rather
2123        than at the "end" then the record id's will not be sorted.
2124        <BR>
2125        In the case where all record id's are always sorted on a page, then
2126        this routine can be used by scan's which "lose" their position because
2127        the row they have as a position was purged. They can reposition their
2128        scan at the "next" row after the row that is now missing from the table.
2129        <BR>
2130        This method returns the record regardless of its deleted status.
2131        <BR>
2132        MT - latched
2133
2134        @param recordId record id of the first record on the page with a
2135                         record id higher than the one passed in. If no
2136                         such record exists, -1 is returned.
2137    */

2138    private int findNextRecordById(int recordId)
2139    {
2140        if (SanityManager.DEBUG)
2141        {
2142            SanityManager.ASSERT(isLatched());
2143        }
2144
2145        int maxSlot = recordCount();
2146
2147        for (int slot = FIRST_SLOT_NUMBER; slot < maxSlot; slot++)
2148        {
2149            if (getHeaderAtSlot(slot).getId() > recordId)
2150            {
2151                return(slot);
2152            }
2153        }
2154
2155        return -1;
2156    }
2157
2158    /**
2159        Copy num_rows from srcPage, src_slot into this page starting at dest_slot.
2160        This is destination page of the the copy half of copy and Purge.
2161
2162        @see Page#copyAndPurge
2163     */

2164    private void copyInto(BasePage srcPage, int src_slot, int num_rows,
2165                          int dest_slot)
2166         throws StandardException
2167    {
2168        if ((dest_slot < 0) || dest_slot > recordCount)
2169        {
2170            throw StandardException.newException(
2171                    SQLState.DATA_SLOT_NOT_ON_PAGE);
2172        }
2173
2174        RawTransaction t = owner.getTransaction();
2175
2176        // get num_rows row locks, need to predict what those recordIds will be
2177

2178        int[] recordIds = new int[num_rows];
2179
2180        PageKey pageId = getPageId(); // RESOLVE - MT problem ?
2181

2182        // get new recordIds for the rows from this page
2183
// RESOLVE: we should also record the amount of reserved space
2184

2185        for (int i = 0; i < num_rows; i++)
2186        {
2187            if (i == 0)
2188                recordIds[i] = newRecordId();
2189            else
2190                recordIds[i] = newRecordId(recordIds[i-1]);
2191
2192            RecordHandle handle = new RecordId(pageId, recordIds[i], i);
2193            owner.getLockingPolicy().lockRecordForWrite(t, handle, false, true);
2194        }
2195
2196        // RESOLVE: need try block here to invalidate self and crash the system
2197
owner.getActionSet().actionCopyRows(t, this, srcPage,
2198                                                  dest_slot, num_rows, src_slot,
2199                                                  recordIds);
2200    }
2201
2202    /**
2203        Remove record at slot.
2204        <p>
2205        Remove the slot at the in-memory slot table, i.e.,
2206        slots from 0 to deleteSlot-1 is untouched, deleteSlot is removed from
2207        in memory slot table, deleteSlot+1 .. recordCount()-1 move to
2208        down one slot.
2209
2210        <BR>
2211        MT - latched
2212    */

2213    protected void removeAndShiftDown(int slot)
2214    {
2215        if (SanityManager.DEBUG) {
2216            SanityManager.ASSERT(isLatched());
2217
2218            SanityManager.ASSERT(slot >= 0 && slot < recordCount);
2219        }
2220
2221        // just copy the records down in the array (copying over the slot
2222
// entry that is being eliminated) and null out the last entry,
2223
// it is ok for the array to be larger than necessary.
2224
//
2225
// source of copy: slot + 1
2226
// dest of copy: slot
2227
// length of copy: (length of array - source of copy)
2228
System.arraycopy(
2229            headers, slot + 1, headers, slot, headers.length - (slot + 1));
2230        headers[headers.length - 1] = null;
2231
2232        recordCount--;
2233    }
2234
2235
2236    /**
2237        Shift all records in the in-memory slot table up one slot,
2238        starting at and including the record in slot 'low'
2239        A new slot is added to accomdate the move.
2240
2241        <BR>
2242        MT - latched
2243    */

2244    protected StoredRecordHeader shiftUp(int low)
2245    {
2246
2247        if (SanityManager.DEBUG)
2248        {
2249            SanityManager.ASSERT(isLatched());
2250
2251            if ((low < 0) || (low > recordCount))
2252            {
2253                SanityManager.THROWASSERT(
2254                    "shiftUp failed, low must be between 0 and recordCount." +
2255                    " low = " + low + ", recordCount = " + recordCount +
2256                    "\n page = " + this);
2257            }
2258        }
2259
2260        if (low < headers.length)
2261        {
2262            // just copy the records up in the array (copying over the slot
2263
// entry that is being eliminated) and null out the entry at "low",
2264
// it is ok for the array to be shorter than necessary.
2265
//
2266
// This code throws away the "last" entry in
2267
// the array, which will cause a record header cache miss if it
2268
// is needed. This delays the object allocation of a new array
2269
// object until we really need that entry, vs. doing it on the
2270
// insert.
2271
//
2272
// source of copy: low
2273
// dest of copy: low + 1
2274
// length of copy: (length of array - dest of copy)
2275

2276
2277            // adding in the middle
2278
System.arraycopy(
2279                headers, low, headers, low + 1, headers.length - (low + 1));
2280
2281            headers[low] = null;
2282        }
2283
2284        return(null);
2285    }
2286
2287
2288
2289    /**
2290        Try to compact this record. Deleted record are treated the same way as
2291        nondeleted record. This page must not be an overflow page. The record
2292        may already have been purged from the page.
2293
2294        <P>
2295        <B>Locking Policy</B>
2296        <P>
2297        No locks are obtained.
2298
2299        <BR>
2300        MT - latched
2301
2302        <P>
2303        <B>NOTE : CAVEAT </B><BR>
2304        This operation will physically get rid of any reserved space this
2305        record may have, or it may compact the record by merging strung out row
2306        pieces together. Since the freed reserved space is immediately usable
2307        by other transactions which latched the page, it is only safe to use
2308        this operation if the caller knows that it has exclusive access to the
2309        page for the duration of the transaction, i.e., effectively holding a
2310        page lock on the page, AND that the record has no uncommitted
2311        updates.
2312
2313      @param handle Handle to deleted or non-deleted record
2314      @see ContainerHandle#compactRecord
2315
2316      @exception StandardException Standard Cloudscape error policy
2317    */

2318    public void compactRecord(RecordHandle handle) throws StandardException
2319    {
2320        if (SanityManager.DEBUG) {
2321            SanityManager.ASSERT(isLatched());
2322        }
2323
2324        if (!owner.updateOK())
2325        {
2326            throw StandardException.newException(
2327                    SQLState.DATA_CONTAINER_READ_ONLY);
2328        }
2329
2330        if (handle.getId() < RecordHandle.FIRST_RECORD_ID)
2331        {
2332            throw StandardException.newException(
2333                    SQLState.DATA_INVALID_RECORD_HANDLE, handle);
2334        }
2335
2336        if (handle.getPageNumber() != getPageNumber())
2337        {
2338            throw StandardException.newException(
2339                    SQLState.DATA_WRONG_PAGE_FOR_HANDLE, handle);
2340        }
2341
2342        if (isOverflowPage())
2343        {
2344            throw StandardException.newException(
2345                    SQLState.DATA_UNEXPECTED_OVERFLOW_PAGE, handle);
2346        }
2347
2348        int slot = findRecordById(handle.getId(), handle.getSlotNumberHint());
2349
2350        if (slot >= 0)
2351        {
2352            compactRecord(owner.getTransaction(), slot, handle.getId());
2353        }
2354        // else record gone, no compaction necessary
2355
}
2356
2357
2358    /*
2359    ** Methods that read/store records/fields based upon calling methods
2360    ** a sub-calls provides to do the actual storage work.
2361    */

2362
2363    /*
2364    ** Page LastLog Instant control
2365    */

2366
2367    public final LogInstant getLastLogInstant()
2368    {
2369        return lastLog;
2370    }
2371
2372    protected final void clearLastLogInstant() {
2373        lastLog = null;
2374    }
2375
2376    protected final void updateLastLogInstant(LogInstant instant)
2377    {
2378        if (SanityManager.DEBUG) {
2379            SanityManager.ASSERT(isLatched());
2380        }
2381
2382        // we should not null out the log instant even if this page is being
2383
// updated by a non-logged action, there may have been logged action
2384
// before this and we don't want to loose that pointer
2385
if (instant != null)
2386            lastLog = instant;
2387    }
2388
2389    /*
2390    ** Page Version control
2391    */

2392
2393    /**
2394        Return the current page version.
2395    */

2396    public final long getPageVersion()
2397    {
2398        return pageVersion;
2399    }
2400
2401    /**
2402        increment the version by one and return the new version.
2403    */

2404    protected final long bumpPageVersion()
2405    {
2406        if (SanityManager.DEBUG) {
2407            SanityManager.ASSERT(isLatched());
2408        }
2409        return ++pageVersion;
2410    }
2411
2412    /**
2413        set it when the page is read from disk.
2414
2415        <BR> MT - single thread required - Only called while the page has no identity which
2416        requires that only a single caller can be accessing it.
2417    */

2418    public final void setPageVersion(long v)
2419    {
2420        pageVersion = v;
2421    }
2422
2423
2424    /**
2425        Set page status based on passed in status flag.
2426    */

2427    protected void setPageStatus(byte status)
2428    {
2429        pageStatus = status;
2430    }
2431
2432
2433    /**
2434        Get the page status, one of the values in the above page status flag
2435    */

2436    public byte getPageStatus()
2437    {
2438        return pageStatus;
2439    }
2440
2441
2442    /*
2443    ** abstract methods that an implementation must provide.
2444    **
2445    ** <BR> MT - latched, page is latched when these methods are called.
2446    */

2447
2448    /**
2449     * Read the record at the given slot into the given row.
2450     * <P>
2451     * This reads and initializes the columns in the row array from the raw
2452     * bytes stored in the page associated with the given slot. If validColumns
2453     * is non-null then it will only read those columns indicated by the bit
2454     * set, otherwise it will try to read into every column in row[].
2455     * <P>
2456     * If there are more columns than entries in row[] then it just stops after
2457     * every entry in row[] is full.
2458     * <P>
2459     * If there are more entries in row[] than exist on disk, the requested
2460     * excess columns will be set to null by calling the column's object's
2461     * restoreToNull() routine
2462     * (ie. ((Object) column).restoreToNull() ).
2463     * <P>
2464     * If a qualifier list is provided then the row will only be read from
2465     * disk if all of the qualifiers evaluate true. Some of the columns may
2466     * have been read into row[] in the process of evaluating the qualifier.
2467     *
2468     * <BR> MT - latched, page is latched when this methods is called.
2469     *
2470     *
2471     * @param slot the slot number
2472     * @param row (out) filled in sparse row
2473     * @param fetchDesc A set of information about the fetch: what
2474     * columns to fetch, any qualifiers, ...
2475     * @param rh the record handle for the row at top level,
2476     * and is used in OverflowInputStream to lock the
2477     * row for Blobs/Clobs.
2478     * @param isHeadRow Is the head row portion of the row, false if
2479     * a long row and the 2-N'th portion of the long
2480     * row.
2481     *
2482     * @return false if a qualifier_list is provided and the row does not
2483     * qualifier (no row read in that case), else true.
2484     *
2485     * @exception StandardException Standard Cloudscape error policy
2486     **/

2487    protected abstract boolean restoreRecordFromSlot(
2488    int slot,
2489    Object JavaDoc[] row,
2490    FetchDescriptor fetchDesc,
2491    RecordHandle rh,
2492    StoredRecordHeader recordHeader,
2493    boolean isHeadRow)
2494        throws StandardException;
2495
2496
2497    /**
2498        Read portion of a log record at the given slot into the given byteHolder.
2499
2500        <BR> MT - latched, page is latched when this methods is called.
2501
2502
2503        @exception StandardException Standard Cloudscape error policy
2504    */

2505    protected abstract void restorePortionLongColumn(OverflowInputStream fetchStream)
2506        throws StandardException, IOException JavaDoc;
2507
2508    /**
2509        Create a new record identifier.
2510
2511        <BR> MT - latched, page is latched when this methods is called.
2512
2513        @exception StandardException Standard Cloudscape error policy
2514    */

2515    public abstract int newRecordId() throws StandardException;
2516
2517    /**
2518        Create a new record identifier, and bump to next recordid.
2519
2520        <BR> MT - latched, page is latched when this methods is called.
2521
2522        @exception StandardException Standard Cloudscape error policy
2523    */

2524    public abstract int newRecordIdAndBump() throws StandardException;
2525
2526    /**
2527        Create a new record identifier, the passed in one is the last one created.
2528        Use this method to collect and reserve multiple recordIds in one
2529        stroke. Given the same input recordId, the subclass MUST return the
2530        same recordId every time.
2531
2532        <BR> MT - latched, page is latched when this methods is called.
2533
2534        @exception StandardException Standard Cloudscape error policy
2535    */

2536    protected abstract int newRecordId(int recordId) throws StandardException;
2537
2538    /**
2539        Is there space for copying this many rows which takes this many bytes
2540        on the page
2541
2542        <BR> MT - latched, page is latched when this methods is called.
2543
2544        @exception StandardException Standard Cloudscape policy.
2545    */

2546    public abstract boolean spaceForCopy(int num_rows, int[] spaceNeeded)
2547         throws StandardException;
2548
2549    /**
2550        Return the total number of bytes used, reserved, or wasted by the
2551        record at this slot.
2552
2553        <BR> MT - latched, page is latched when this methods is called.
2554
2555        @exception StandardException Standard Cloudscape policy.
2556    */

2557    public abstract int getTotalSpace(int slot) throws StandardException;
2558
2559    /**
2560        Return the total number of bytes reserved by the
2561        record at this slot.
2562
2563        <BR> MT - latched, page is latched when this methods is called.
2564
2565        @exception IOException Thrown by InputStream methods potential I/O errors
2566    */

2567    public abstract int getReservedCount(int slot) throws IOException JavaDoc;
2568
2569    /*
2570    ** Methods that our super-class (BasePage) requires we implement.
2571    ** Here we only implement the methods that correspond to the logical
2572    ** operations that require logging, any other methods that are storage
2573    ** specific we leave to our sub-class.
2574    **
2575    ** All operations that are logged must bump this page's version number
2576    ** and update this page's last log instant.
2577    ** These should be sanity checked on each logAndDo (similarly, it should
2578    ** be checked in CompensationOperation.doMe)
2579    */

2580
2581
2582    /*
2583    ** Methods that any sub-class must implement. These allow generic log operations.
2584    */

2585
2586    /**
2587        Get the stored length of a record. This must match the amount of data
2588        written by logColumn and logField.
2589
2590        <BR> MT - latched - page latch must be held
2591    */

2592
2593    public abstract int getRecordLength(int slot) throws IOException JavaDoc;
2594
2595    /**
2596        Restore a storable row from a InputStream that was used to
2597        store the row after a logRecord call.
2598
2599        <BR> MT - latched - page latch must be held
2600
2601        @exception StandardException Standard Cloudscape error policy
2602        @exception IOException object exceeds the available data in the stream.
2603
2604    */

2605    public abstract void restoreRecordFromStream(
2606    LimitObjectInput in,
2607    Object JavaDoc[] row)
2608         throws StandardException, IOException JavaDoc;
2609
2610    /**
2611        Log a currently stored record to the output stream.
2612        The logged version of the record must be readable by storeRecord.
2613
2614        <BR> MT - latched - page latch must be held
2615
2616
2617        @param slot Slot number the record is stored in.
2618        @param flag LOG_RECORD_*, the reason for logging the record.
2619        @param recordId Record identifier of the record.
2620        @param validColumns which columns needs to be logged
2621        @param out Where to write the logged form.
2622        @param headRowHandle the recordHandle of the head row piece, used
2623                        for post commit cleanup for update.
2624
2625        @exception StandardException Standard Cloudscape error policy
2626    */

2627    public abstract void logRecord(int slot, int flag, int recordId,
2628                                   FormatableBitSet validColumns, OutputStream out,
2629                                   RecordHandle headRowHandle)
2630        throws StandardException, IOException JavaDoc;
2631
2632
2633    /**
2634        Log the row that will be stored at the given slot to the given OutputStream.
2635        The logged form of the Row must be readable by storeRecord.
2636
2637        <BR> MT - latched - page latch must be held
2638
2639        @param slot Slot number the record will be stored in.
2640        @param forInsert True if the row is being logged for an insert,
2641                                false for an update.
2642        @param recordId Record identifier of the record.
2643        @param row The row version of the record.
2644        @param validColumns FormatableBitSet of which columns in row are valid,
2645                                null indicates all are valid
2646        @param out Where to write the logged form.
2647        @param startColumn The first column that is being logged in this row.
2648                                This is used when logging portion of long rows.
2649        @param insertFlag To indicate whether the insert would allow overflow.
2650        @param realStartColumn This is used when a long column is detected.
2651                                Portion of the row may already be logged and stored
2652                                in the 'out' buffer. After we log the long column,
2653                                the saved buffer was passed here, and we need to
2654                                continue to log the row. realStartColumn is the starting
2655                                column for the continuation of the logRow operation.
2656                                Pass in (-1) if realStartColumn is not significant.
2657        @param realSpaceOnPage Being used in conjunction with realStartColumn,
2658                                to indicate the real free space left on the page.
2659
2660        @exception StandardException Standard Cloudscape error policy
2661    */

2662    public abstract int logRow(
2663    int slot,
2664    boolean forInsert,
2665    int recordId,
2666    Object JavaDoc[] row,
2667    FormatableBitSet validColumns,
2668    DynamicByteArrayOutputStream out,
2669    int startColumn,
2670    byte insertFlag,
2671    int realStartColumn,
2672    int realSpaceOnPage,
2673    int overflowThreshold)
2674        throws StandardException, IOException JavaDoc;
2675
2676    /**
2677        Log a currently stored field.
2678        The logged version of the field must be readable by storeField.
2679
2680        <BR> MT - latched - page latch must be held
2681
2682        @param slot Slot number the record is stored in.
2683        @param fieldNumber Number of the field (starts at 0).
2684        @param out Where to write the logged form.
2685
2686        @exception StandardException Standard Cloudscape error policy
2687    */

2688    public abstract void logField(int slot, int fieldNumber, OutputStream out)
2689        throws StandardException, IOException JavaDoc;
2690    /**
2691        Log a to be stored column.
2692
2693        <BR> MT - latched - page latch must be held
2694
2695        @param slot slot of the current record
2696        @param fieldId field number of the column being updated
2697        @param column column version of the field.
2698        @param out Where to write the logged form.
2699
2700        @exception StandardException Standard Cloudscape error policy
2701    */

2702    public abstract void logColumn(
2703    int slot,
2704    int fieldId,
2705    Object JavaDoc column,
2706    DynamicByteArrayOutputStream out,
2707    int overflowThreshold)
2708        throws StandardException, IOException JavaDoc;
2709
2710    /**
2711        Log a to be stored long column. return -1 when done.
2712
2713        <BR> MT - latched - page latch must be held
2714
2715        @param slot slot of the current record
2716        @param recordId the id of the long column record
2717        @param column column version of the field.
2718        @param out Where to write the logged form.
2719
2720        @exception StandardException Standard Cloudscape error policy
2721    */

2722    public abstract int logLongColumn(
2723    int slot,
2724    int recordId,
2725    Object JavaDoc column,
2726    DynamicByteArrayOutputStream out)
2727        throws StandardException, IOException JavaDoc;
2728
2729    /**
2730        Read a previously stored record written by logRecord or logRow and store
2731        it on the data page at the given slot with the given record identifier.
2732        Any previously stored record must be replaced.
2733
2734        <BR> MT - latched - page latch must be held
2735
2736        @exception StandardException Standard Cloudscape error policy
2737        @exception IOException Thrown by InputStream methods potential I/O errors
2738        while writing the page
2739        
2740    */

2741    public abstract void storeRecord(LogInstant instant, int slot, boolean forInsert, ObjectInput in)
2742        throws StandardException, IOException JavaDoc;
2743
2744    /**
2745        Read a previously stored field written by logField or logColumn and store
2746        it on the data page at thge given slot with the given record identifier
2747        and field number. Any previously stored field is replaced.
2748
2749        <BR> MT - latched - page latch must be held
2750
2751        @exception StandardException Standard Cloudscape error policy
2752        @exception IOException Thrown by InputStream methods and potential I/O errors
2753        while writing the page.
2754    */

2755    public abstract void storeField(LogInstant instant, int slot,
2756                                       int fieldId,
2757                                       ObjectInput in)
2758        throws StandardException, IOException JavaDoc;
2759
2760    /**
2761        Reserve the required number of bytes for the record in the specified slot.
2762
2763        <BR> MT - latched - page latch must be held
2764
2765        @exception StandardException Standard Cloudscape error policy
2766        @exception IOException Thrown by InputStream methods and potential I/O errors
2767        while writing the page.
2768    */

2769    public abstract void reserveSpaceForSlot(LogInstant instant, int slot, int spaceToReserve)
2770        throws StandardException, IOException JavaDoc;
2771
2772
2773    /**
2774        Skip a previously stored field written by logField or logColumn.
2775
2776        <BR> MT - latched - page latch must be held
2777        
2778        @exception StandardException Standard Cloudscape error policy
2779        @exception IOException Thrown by InputStream methods
2780
2781    */

2782    public abstract void skipField(ObjectInput in)
2783         throws StandardException, IOException JavaDoc;
2784
2785    public abstract void skipRecord(ObjectInput in) throws StandardException, IOException JavaDoc;
2786
2787    /**
2788        Set the delete status of a record from the page.
2789
2790        <BR> MT - latched - page latch must be held
2791
2792        @param slot the slot to delete or undelete
2793        @param delete set delete status to this value
2794
2795        @exception StandardException Standard Cloudscape error policy
2796        @exception IOException IO error accessing page
2797    */

2798    public abstract void setDeleteStatus(LogInstant instant, int slot, boolean delete)
2799         throws StandardException, IOException JavaDoc;
2800
2801    /**
2802        Purge a record from the page.
2803
2804        <BR> MT - latched - page latch must be held
2805
2806        @param slot the slot to purge
2807        @param recordId the id of the record that is to be purged
2808
2809        @exception StandardException Standard Cloudscape error policy
2810        @exception IOException Thrown by potential I/O errors
2811        while writing the page.
2812    */

2813    public abstract void purgeRecord(LogInstant instant, int slot,
2814                                        int recordId)
2815        throws StandardException, IOException JavaDoc;
2816
2817    /**
2818        Subclass implementation of compactRecord.
2819        @see BasePage#compactRecord
2820        @exception StandardException Standard Cloudscape error policy
2821     */

2822    protected abstract void compactRecord(RawTransaction t, int slot, int recordId)
2823         throws StandardException;
2824
2825    /**
2826        Set the page status underneath a log record
2827
2828        <BR> MT - latched - page latch must be held
2829
2830        @param instant the log instant of the log record
2831        @param status the page status
2832
2833        @exception StandardException Standard Cloudscape error policy
2834    */

2835    public abstract void setPageStatus(LogInstant instant, byte status)
2836        throws StandardException;
2837
2838
2839    /**
2840        initialize a page for the first time or for reuse
2841
2842        All subtypes are expected to overwrite this method if it has something to clean up
2843
2844        @exception StandardException Standard Cloudscape error policy
2845    */

2846    public abstract void initPage(LogInstant instant, byte status,
2847                                  int recordId, boolean overflow, boolean reuse)
2848         throws StandardException;
2849
2850    /**
2851        Set the reserved space for this row to value.
2852        @exception StandardException Standard Cloudscape error policy
2853    */

2854    public abstract void setReservedSpace(LogInstant instant, int slot, int value)
2855         throws StandardException, IOException JavaDoc;
2856
2857    /**
2858        Return true if the page is an overflow page, false if not.
2859        For implementation that don't have overflow pages, return false.
2860    */

2861    public abstract boolean isOverflowPage();
2862
2863    /**
2864        Returns false if an insert is not to be allowed in the page.
2865    */

2866    public abstract boolean allowInsert();
2867
2868    /**
2869        Returns true if an insert is allowed in the page and the page is
2870        relatively unfilled - let specific implementation decide what
2871        relatively unfilled means
2872    */

2873    public abstract boolean unfilled();
2874
2875    /**
2876        Set the number of rows in the container - the page uses this to decide
2877        whether it needs to aggressive set the container's row count when it
2878        changes.
2879     */

2880    public abstract void setContainerRowCount(long count);
2881
2882    /*
2883     * returns the page data array, that is actually written to the disk.
2884     */

2885    protected abstract byte[] getPageArray() throws StandardException;
2886                                                 
2887    /*
2888    ** Debugging methods
2889    */

2890
2891    /** Debugging, print slot table information */
2892    protected String JavaDoc slotTableToString()
2893    {
2894        String JavaDoc str = null;
2895
2896        if (SanityManager.DEBUG)
2897        {
2898            StoredRecordHeader rh;
2899            str = new String JavaDoc();
2900
2901            for (int slot = FIRST_SLOT_NUMBER; slot < recordCount; slot++) {
2902                rh = getHeaderAtSlot(slot);
2903                if (rh != null)
2904                    str += "Slot " + slot + " recordId " + rh.getId();
2905                else
2906                    str += "Slot " + slot + " null record";
2907                str += "\n";
2908            }
2909        }
2910        return str;
2911    }
2912
2913    /**
2914        This lockable wants to participate in the Virtual Lock table.
2915     */

2916    public boolean lockAttributes(int flag, Hashtable JavaDoc attributes)
2917    {
2918        if (SanityManager.DEBUG)
2919        {
2920            SanityManager.ASSERT(attributes != null,
2921                "cannot call lockProperties with null attribute list");
2922        }
2923
2924        if ((flag & VirtualLockTable.LATCH) == 0)
2925            return false;
2926
2927        // by the time this is called, the page may be unlatched.
2928
PageKey pageId = identity;
2929
2930        // not latched
2931
if (pageId == null)
2932            return false;
2933
2934        attributes.put(VirtualLockTable.CONTAINERID,
2935                       new Long JavaDoc(pageId.getContainerId().getContainerId()));
2936        attributes.put(VirtualLockTable.LOCKNAME, pageId.toString());
2937        attributes.put(VirtualLockTable.LOCKTYPE, "LATCH");
2938
2939        // don't new unecesary things for now
2940
// attributes.put(VirtualLockTable.SEGMENTID, new Long(pageId.getContainerId().getSegmentId()));
2941
// attributes.put(VirtualLockTable.PAGENUM, new Long(pageId.getPageNumber()));
2942

2943        return true;
2944    }
2945        
2946
2947}
2948
Popular Tags