001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.StoredRecordHeader
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021: package org.apache.derby.impl.store.raw.data;
022:
023: import org.apache.derby.iapi.services.sanity.SanityManager;
024:
025: import org.apache.derby.iapi.store.raw.PageKey;
026: import org.apache.derby.iapi.store.raw.RecordHandle;
027:
028: import java.io.IOException;
029: import java.io.EOFException;
030:
031: import java.io.InputStream;
032: import java.io.OutputStream;
033:
034: import org.apache.derby.iapi.services.io.CompressedNumber;
035:
036: /**
037: A class StoredPage uses to cache record headers by passing instances
038: to BasePage, and to write stored versions of record headers.
039:
040: Format
041:
042: <PRE>
043:
044: 1 byte - status
045: compressed int - record identifier
046:
047: compressed long - overflow page } only if hasOverflow() is true
048: compressed int - overflow id } " " "
049:
050: compressed int - first field } only if hasFirstField set - otherwise 0
051:
052: compressed int - number of fields in this portion - only if hasOverflow()
053: is false OR hasFirstField is true - otherwise 0
054: </PRE>
055:
056: */
057:
058: public final class StoredRecordHeader {
059:
060: /**************************************************************************
061: * Constants of the class
062: **************************************************************************
063: */
064:
065: /**
066: * Status bits for the record header:
067: *
068: * RECORD_INITIAL - used when record header is first initialized
069: * RECORD_DELETED - used to indicate the record has been deleted
070: * RECORD_OVERFLOW - used to indicate the record has been
071: * overflowed, it will point to the overflow
072: * page and ID
073: * RECORD_HAS_FIRST_FIELD - used to indicate that firstField is stored.
074: * When RECORD_OVERFLOW and
075: * RECORD_HAS_FIRST_FIELD both are set, part of
076: * record is on the page, the record header
077: * also stores the overflow point to the next
078: * part of the record.
079: * RECORD_VALID_MASK - A mask of valid bits that can be set
080: * currently, such that the following assert can
081: * be made:
082: * ASSERT((status & ~RECORD_VALID_MASK) == 0))
083: **/
084: public static final int RECORD_INITIAL = 0x00;
085: public static final int RECORD_DELETED = 0x01;
086: public static final int RECORD_OVERFLOW = 0x02;
087: public static final int RECORD_HAS_FIRST_FIELD = 0x04;
088: public static final int RECORD_VALID_MASK = 0x0f;
089:
090: /**************************************************************************
091: * Fields of the class
092: **************************************************************************
093: */
094:
095: /**
096: * Actual identifier of the record
097: *
098: * <BR> MT - Mutable
099: **/
100: protected int id;
101:
102: /**
103: * Status of the record.
104: *
105: * See above for description of fields:
106: * RECORD_INITIAL
107: * RECORD_DELETED
108: * RECORD_OVERFLOW
109: * RECORD_HAS_FIRST_FIELD
110: * RECORD_VALID_MASK
111: *
112: * <BR> MT - Mutable - single thread required.
113: **/
114: protected int status;
115:
116: /**
117: * number of fields in the row.
118: **/
119: protected int numberFields;
120:
121: /**
122: * A record handle that can represent the record, may be null.
123: **/
124: protected RecordHandle handle;
125:
126: /**
127: * If (hasOverflow()) then this is the id of the row on page overflowPage
128: * where the next portion of the row can be found. In this case there
129: * are no "real" fields on this page. This situation comes about if a
130: * row has been updated such that the real first field no longer fits on
131: * the head page.
132: **/
133: protected int overflowId;
134:
135: /**
136: * If (hasOverflow()) then this is the page where where the next portion of
137: * the row can be found. In this case there are no "real" fields on this
138: * page.
139: **/
140: protected long overflowPage;
141:
142: /**
143: * if (hasFirstField()) then this field is the number of the column in
144: * the orginal row which is now stored as the first field in this row. This
145: * row is 2nd through N'th portion of a long row.
146: *
147: * For example if a row has its first 3 fields on page 0 and its next 3
148: * fields on page 1, then the record header of the row portion on page 1
149: * will have hasFirstField() set to true, and the value would be 4,
150: * indicating that the 4th field of the row is stored as the 1st field of
151: * the partial row portion stored on page 1.
152: **/
153: protected int firstField;
154:
155: /**************************************************************************
156: * Constructors for This class:
157: **************************************************************************
158: */
159: public StoredRecordHeader() {
160: }
161:
162: public StoredRecordHeader(int id, int numberFields) {
163: setId(id);
164: setNumberFields(numberFields);
165: }
166:
167: public StoredRecordHeader(byte data[], int offset) {
168: read(data, offset);
169: }
170:
171: public StoredRecordHeader(StoredRecordHeader loadTargetFrom) {
172: this .status = loadTargetFrom.status;
173: this .id = loadTargetFrom.id;
174: this .numberFields = loadTargetFrom.numberFields;
175: handle = null;
176:
177: overflowId = loadTargetFrom.overflowId;
178: overflowPage = loadTargetFrom.overflowPage;
179: firstField = loadTargetFrom.firstField;
180: }
181:
182: /**************************************************************************
183: * Public Accessor "Get" Methods of This class:
184: **************************************************************************
185: */
186:
187: /**
188: * Get a record handle for the record.
189: * <p>
190: *
191: * <BR> MT - single thread required
192: **/
193: protected RecordHandle getHandle(PageKey pageId, int current_slot) {
194: if (handle == null)
195: handle = new RecordId(pageId, id, current_slot);
196:
197: return handle;
198: }
199:
200: /**
201: * Get the record identifier
202: *
203: * <BR> MT - thread safe
204: **/
205: public final int getId() {
206: return id;
207: }
208:
209: public int getNumberFields() {
210: return numberFields;
211: }
212:
213: public long getOverflowPage() {
214: return overflowPage;
215: }
216:
217: public int getOverflowId() {
218: return overflowId;
219: }
220:
221: public int getFirstField() {
222: return firstField;
223: }
224:
225: public final boolean hasOverflow() {
226: return ((status & RECORD_OVERFLOW) == RECORD_OVERFLOW);
227: }
228:
229: protected final boolean hasFirstField() {
230: return ((status & RECORD_HAS_FIRST_FIELD) == RECORD_HAS_FIRST_FIELD);
231: }
232:
233: /**
234: * Get the deleted state of the record.
235: * <p>
236: *
237: * <BR> MT - single thread required
238: **/
239: public final boolean isDeleted() {
240: return ((status & RECORD_DELETED) == RECORD_DELETED);
241: }
242:
243: /**
244: * return the size of the record header.
245: * <p>
246: * Calculates the size of the record header, mostly used to allow a
247: * reader to skip over the record header and position on the 1st field
248: * of the record.
249: * <p>
250: * This low level routine is performance critical to processing lots of
251: * rows, so calls to CompressNumber have been hand inlined.
252: *
253: * @return The length of the record header.
254: *
255: * @exception StandardException Standard exception policy.
256: **/
257: public int size() {
258: // account for length of fieldDataLength field stored as a compressed
259: // int plus one byte for status.
260: //
261: // int len = CompressedNumber.sizeInt(id) + 1;
262: int len = (id <= CompressedNumber.MAX_COMPRESSED_INT_ONE_BYTE) ? 2
263: : (id <= CompressedNumber.MAX_COMPRESSED_INT_TWO_BYTES) ? 3
264: : 5;
265:
266: if ((status & (RECORD_OVERFLOW | RECORD_HAS_FIRST_FIELD)) == 0) {
267: // usual case, not a record overflow and does not have first field
268: len += (numberFields <= CompressedNumber.MAX_COMPRESSED_INT_ONE_BYTE) ? 1
269: : (numberFields <= CompressedNumber.MAX_COMPRESSED_INT_TWO_BYTES) ? 2
270: : 4;
271: } else if ((status & RECORD_OVERFLOW) == 0) {
272: // not overflow, and has first field set.
273:
274: // account for size of the numberFields field + size fo the
275: // firstField field.
276: //
277: // len += (CompressedNumber.sizeInt(numberFields) +
278: // CompressedNumber.sizeInt(firstField);
279: //
280: len += ((numberFields <= CompressedNumber.MAX_COMPRESSED_INT_ONE_BYTE) ? 1
281: : (numberFields <= CompressedNumber.MAX_COMPRESSED_INT_TWO_BYTES) ? 2
282: : 4)
283: +
284:
285: ((firstField <= CompressedNumber.MAX_COMPRESSED_INT_ONE_BYTE) ? 1
286: : (firstField <= CompressedNumber.MAX_COMPRESSED_INT_TWO_BYTES) ? 2
287: : 4);
288: } else {
289: // is an overflow field
290:
291: len += CompressedNumber.sizeLong(overflowPage);
292: len += CompressedNumber.sizeInt(overflowId);
293:
294: if (hasFirstField()) {
295: len += CompressedNumber.sizeInt(firstField);
296: len += CompressedNumber.sizeInt(numberFields);
297: }
298: }
299:
300: return len;
301: }
302:
303: /**************************************************************************
304: * Public Accessor "Set" Methods of This class:
305: **************************************************************************
306: */
307:
308: /**
309: * Set the deleted state of the record.
310: * <p>
311: * return 1, if delete status from not deleted to deleted
312: * return -1, if delete status from deleted to not deleted
313: * return 0, if status unchanged.
314: *
315: * <BR> MT - single thread required
316: **/
317: public int setDeleted(boolean deleteTrue) {
318:
319: int retCode = 0;
320:
321: if (deleteTrue) {
322: if (!isDeleted()) {
323: // setting the bit from not deleted to deleted
324: retCode = 1;
325: status |= RECORD_DELETED;
326: }
327: } else {
328: if (isDeleted()) {
329: // setting the bit from deleted to not deleted
330: retCode = -1;
331: status &= ~RECORD_DELETED;
332: }
333: }
334:
335: return (retCode);
336: }
337:
338: public void setFirstField(int firstField) {
339: this .firstField = firstField;
340: status |= RECORD_HAS_FIRST_FIELD;
341: }
342:
343: public final void setId(int id) {
344: this .id = id;
345: }
346:
347: public void setOverflowDetails(RecordHandle overflowHandle) {
348: this .overflowPage = overflowHandle.getPageNumber();
349: this .overflowId = overflowHandle.getId();
350: }
351:
352: public void setOverflowFields(StoredRecordHeader loadFromTarget) {
353: this .status = (loadFromTarget.status | RECORD_OVERFLOW);
354: this .id = loadFromTarget.id;
355: this .numberFields = loadFromTarget.numberFields;
356: this .firstField = loadFromTarget.firstField;
357: handle = null;
358: }
359:
360: public final void setNumberFields(int numberFields) {
361: this .numberFields = numberFields;
362: }
363:
364: /**************************************************************************
365: * Public Methods implmenting read/write of Storable Interface:
366: **************************************************************************
367: */
368: public int write(OutputStream out) throws IOException {
369: // check consistency of the status field - this has caught
370: // byte writing corruptions in StoredPage in the past.
371: if (SanityManager.DEBUG) {
372: if ((status & ~RECORD_VALID_MASK) != 0)
373: SanityManager
374: .THROWASSERT("Invalid status in StoredRecordHeaader = "
375: + status);
376: }
377:
378: // write status
379: int len = 1;
380: out.write(status);
381:
382: // write id
383: len += CompressedNumber.writeInt(out, id);
384:
385: // write overflow information for overflow record headers
386: if (hasOverflow()) {
387: // if overflow bit is set, then write the overflow pointer info.
388: len += CompressedNumber.writeLong(out, overflowPage);
389: len += CompressedNumber.writeInt(out, overflowId);
390: }
391:
392: // write first field info for long row parts
393: if (hasFirstField()) {
394: len += CompressedNumber.writeInt(out, firstField);
395: }
396:
397: // write number of fields, except in the case of a record header
398: // which is solely a pointer to another row portion.
399: //
400: // see read
401: if (!hasOverflow() || hasFirstField())
402: len += CompressedNumber.writeInt(out, numberFields);
403:
404: return len;
405: }
406:
407: public void read(java.io.ObjectInput in) throws IOException {
408:
409: // read status
410: status = in.read();
411: if (status < 0)
412: throw new EOFException();
413:
414: // check consistency of the status field - this has caught
415: // byte writing corruptions in StoredPage in the past.
416: if (SanityManager.DEBUG) {
417: if ((status & ~RECORD_VALID_MASK) != 0)
418: SanityManager
419: .THROWASSERT("Invalid status in StoredRecordHeader = "
420: + status);
421: }
422:
423: // read the record id
424: id = CompressedNumber.readInt(in);
425:
426: // initialize the overflow pointer based on status.
427: if (hasOverflow()) {
428: overflowPage = CompressedNumber.readLong(in);
429: overflowId = CompressedNumber.readInt(in);
430:
431: } else {
432: overflowPage = 0;
433: overflowId = 0;
434: }
435:
436: // initialize the 1st field overflow pointer based on status.
437: if (hasFirstField()) {
438: firstField = CompressedNumber.readInt(in);
439: } else {
440: firstField = 0;
441: }
442:
443: // In releases prior to 1.3 an overflow record was handled
444: // by an overflow header pointing to a complete record on
445: // another page. This header had the has overflow bit set but not
446: // the has first field bit. This header also did not have the
447: // number of fields written out, but it can be seen as
448: // a header with 0 fields and a first field of 0.
449: if (!hasOverflow() || hasFirstField())
450: numberFields = CompressedNumber.readInt(in);
451: else
452: numberFields = 0;
453:
454: handle = null;
455: }
456:
457: private int readId(byte[] data, int offset) {
458: int value = data[offset++];
459:
460: if ((value & ~0x3f) == 0) {
461: // value stored in this byte.
462: id = value;
463:
464: return (1);
465: } else if ((value & 0x80) == 0) {
466: // value is stored in 2 bytes. only use low 6 bits from 1st byte.
467:
468: id = (((value & 0x3f) << 8) | (data[offset] & 0xff));
469:
470: return (2);
471: } else {
472: // value is stored in 4 bytes. only use low 7 bits from 1st byte.
473: id = ((value & 0x7f) << 24)
474: | ((data[offset++] & 0xff) << 16)
475: | ((data[offset++] & 0xff) << 8)
476: | ((data[offset] & 0xff));
477:
478: return (4);
479: }
480: }
481:
482: private int readOverFlowPage(byte[] data, int offset) {
483: int int_value = data[offset++];
484:
485: if ((int_value & ~0x3f) == 0) {
486: // test for small case first - assuming this is usual case.
487: // this is stored in 2 bytes.
488:
489: overflowPage = ((int_value << 8) | (data[offset] & 0xff));
490:
491: return (2);
492: } else if ((int_value & 0x80) == 0) {
493: // value is stored in 4 bytes. only use low 6 bits from 1st byte.
494:
495: overflowPage = ((int_value & 0x3f) << 24)
496: | ((data[offset++] & 0xff) << 16)
497: | ((data[offset++] & 0xff) << 8)
498: | ((data[offset] & 0xff));
499:
500: return (4);
501:
502: } else {
503: // value is stored in 8 bytes. only use low 6 bits from 1st byte.
504: overflowPage = (((long) (int_value & 0x7f)) << 56)
505: | (((long) (data[offset++] & 0xff)) << 48)
506: | (((long) (data[offset++] & 0xff)) << 40)
507: | (((long) (data[offset++] & 0xff)) << 32)
508: | (((long) (data[offset++] & 0xff)) << 24)
509: | (((long) (data[offset++] & 0xff)) << 16)
510: | (((long) (data[offset++] & 0xff)) << 8)
511: | (((long) (data[offset] & 0xff)));
512:
513: return (8);
514: }
515: }
516:
517: private int readOverFlowId(byte[] data, int offset) {
518: int value = data[offset++];
519:
520: if ((value & ~0x3f) == 0) {
521: // length stored in this byte.
522: overflowId = value;
523:
524: return (1);
525: } else if ((value & 0x80) == 0) {
526: // length is stored in 2 bytes. only use low 6 bits from 1st byte.
527:
528: overflowId = (((value & 0x3f) << 8) | (data[offset] & 0xff));
529:
530: return (2);
531: } else {
532: // length is stored in 4 bytes. only use low 7 bits from 1st byte.
533: overflowId = ((value & 0x7f) << 24)
534: | ((data[offset++] & 0xff) << 16)
535: | ((data[offset++] & 0xff) << 8)
536: | ((data[offset] & 0xff));
537:
538: return (4);
539: }
540: }
541:
542: private int readFirstField(byte[] data, int offset) {
543: int value = data[offset++];
544:
545: if ((value & ~0x3f) == 0) {
546: // length stored in this byte.
547: firstField = value;
548:
549: return (1);
550: } else if ((value & 0x80) == 0) {
551: // length is stored in 2 bytes. only use low 6 bits from 1st byte.
552:
553: firstField = (((value & 0x3f) << 8) | (data[offset] & 0xff));
554:
555: return (2);
556: } else {
557: // length is stored in 4 bytes. only use low 7 bits from 1st byte.
558: firstField = ((value & 0x7f) << 24)
559: | ((data[offset++] & 0xff) << 16)
560: | ((data[offset++] & 0xff) << 8)
561: | ((data[offset] & 0xff));
562:
563: return (4);
564: }
565: }
566:
567: private void readNumberFields(byte[] data, int offset) {
568: int value = data[offset++];
569:
570: if ((value & ~0x3f) == 0) {
571: // length stored in this byte.
572: numberFields = value;
573: } else if ((value & 0x80) == 0) {
574: // length is stored in 2 bytes. only use low 6 bits from 1st byte.
575:
576: numberFields = (((value & 0x3f) << 8) | (data[offset] & 0xff));
577: } else {
578: // length is stored in 4 bytes. only use low 7 bits from 1st byte.
579: numberFields = ((value & 0x7f) << 24)
580: | ((data[offset++] & 0xff) << 16)
581: | ((data[offset++] & 0xff) << 8)
582: | ((data[offset] & 0xff));
583: }
584: }
585:
586: private void read(byte[] data, int offset) {
587: status = data[offset++];
588:
589: int value = data[offset++];
590:
591: if ((value & ~0x3f) == 0) {
592: // value stored in this byte.
593: id = value;
594: } else if ((value & 0x80) == 0) {
595: // value is stored in 2 bytes. only use low 6 bits from 1st byte.
596:
597: id = (((value & 0x3f) << 8) | (data[offset++] & 0xff));
598: } else {
599: // value is stored in 4 bytes. only use low 7 bits from 1st byte.
600: id = ((value & 0x7f) << 24)
601: | ((data[offset++] & 0xff) << 16)
602: | ((data[offset++] & 0xff) << 8)
603: | ((data[offset++] & 0xff));
604: }
605:
606: if ((status & (RECORD_OVERFLOW | RECORD_HAS_FIRST_FIELD)) == 0) {
607: // usual case, not a record overflow and does not have first field
608: overflowPage = 0;
609: overflowId = 0;
610: firstField = 0;
611:
612: readNumberFields(data, offset);
613: } else if ((status & RECORD_OVERFLOW) == 0) {
614: // not overflow, and has first field set.
615: overflowPage = 0;
616: overflowId = 0;
617:
618: offset += readFirstField(data, offset);
619:
620: readNumberFields(data, offset);
621: } else {
622: // is an overflow field
623:
624: offset += readOverFlowPage(data, offset);
625: offset += readOverFlowId(data, offset);
626:
627: if (hasFirstField()) {
628: offset += readFirstField(data, offset);
629: readNumberFields(data, offset);
630: } else {
631: firstField = 0;
632: numberFields = 0;
633: }
634: }
635:
636: handle = null;
637:
638: return;
639: }
640:
641: public String toString() {
642: if (SanityManager.DEBUG) {
643: String str = "recordHeader: Id=" + getId();
644:
645: str += "\n isDeleted = " + isDeleted();
646: str += "\n hasOverflow = " + hasOverflow();
647: str += "\n hasFirstField = " + hasFirstField();
648: str += "\n numberFields = " + getNumberFields();
649: str += "\n firstField = " + getFirstField();
650: str += "\n overflowPage = " + getOverflowPage();
651: str += "\n overflowId = " + getOverflowId();
652:
653: return str;
654: } else {
655: return null;
656: }
657: }
658: }
|