001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.StoredFieldHeader
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.store.raw.RecordHandle;
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025:
026: import java.io.IOException;
027: import java.io.EOFException;
028:
029: import java.io.ObjectInput;
030: import java.io.OutputStream;
031:
032: import org.apache.derby.iapi.services.io.ArrayInputStream;
033: import org.apache.derby.iapi.services.io.CompressedNumber;
034:
035: import java.io.InputStream;
036:
037: /**
038: A class to provide static methods to manipulate fields in the field header.
039:
040: A class StoredPage uses to read/write field status and field data length.
041: No attributes exist in this class, this class provides a set of static
042: methods for writing field status and field data length, and for reading
043: field status and field data length.
044:
045: <P><B>Stored Field Header Format</B><BR>
046: The field header is broken into two sections.
047: Only the Status byte is required to be there.
048: <PRE>
049: Field header format:
050: +--------+-------------------+
051: | status | <fieldDataLength> |
052: +--------+-------------------+
053: Overflow page and overflow id are stored as field data.
054: If the overflow bit in status is set, the field data is the overflow
055: information. When the overflow bit is not set in status, then,
056: fieldData is the actually user data for the field.
057: That means, field header consists only field status, and field data length.
058:
059: A non-overflow field:
060: +--------+-------------------+-------------+
061: | status | <fieldDataLength> | <fieldData> |
062: +--------+-------------------+-------------+
063:
064: An overflow field:
065: +--------+-------------------+-----------------+--------------+
066: | status | <fieldDataLength> | <overflow page> | <overflowID> |
067: +--------+-------------------+-----------------+--------------+
068:
069: </PRE>
070: <BR><B>status</B><BR>
071: The status is 1 byte, it indicates the state of the field.
072:
073: A FieldHeader can be in the following states:
074: NULL - if the field is NULL, no field data length is stored
075: OVERFLOW - indicates the field has been overflowed to another page.
076: overflow page and overflow ID is stored at the end of the
077: user data. field data length must be a number greater or
078: equal to 0, indicating the length of the field that is stored
079: on the current page.
080:
081: The format looks like this:
082: +--------+-----------------+---------------+------------+
083: |<status>|<fieldDataLength>|<overflow page>|<overflowID>|
084: +--------+-----------------+---------------+------------+
085:
086: overflowPage will be written as compressed long,
087: overflowId will be written as compressed Int
088:
089: NONEXISTENT - the field no longer exists,
090: e.g. column has been dropped during an alter table
091:
092: EXTENSIBLE - the field is of user defined data type.
093: The field may be tagged.
094:
095: TAGGED - the field is TAGGED if and only if it is EXTENSIBLE.
096:
097: FIXED - the field is FIXED if and only if it is used in the log
098: records for version 1.2 and higher.
099:
100: <BR><B>fieldDataLength</B><BR>
101: The fieldDataLength is only set if the field is not NULL. It is the length
102: of the field that is stored on the current page.
103: The fieldDataLength is a variable length CompressedInt.
104: <BR><B>overflowPage and overflowID</B><BR>
105: The overflowPage is a variable length CompressedLong, overflowID is a
106: variable Length CompressedInt.
107: They are only stored when the field state is OVERFLOW.
108: And they are not stored in the field header.
109: Instead, they are stored at the end of the field data.
110: The reason we do that is to save a copy if the field has to overflow.
111:
112: <BR> MT - Mutable - Immutable identity - Thread Aware
113: */
114: public final class StoredFieldHeader {
115:
116: /**************************************************************************
117: * Constants of the class
118: **************************************************************************
119: */
120:
121: // DO NOT use 0x80, some code reads byte into an int without masking the
122: // sign bit, so do not use the high bit in the byte for a field status.
123: private static final int FIELD_INITIAL = 0x00;
124: public static final int FIELD_NULL = 0x01;
125: public static final int FIELD_OVERFLOW = 0x02;
126: private static final int FIELD_NOT_NULLABLE = 0x04;
127: public static final int FIELD_EXTENSIBLE = 0x08;
128: public static final int FIELD_TAGGED = 0x10;
129: protected static final int FIELD_FIXED = 0x20;
130:
131: public static final int FIELD_NONEXISTENT = (FIELD_NOT_NULLABLE | FIELD_NULL);
132:
133: public static final int STORED_FIELD_HEADER_STATUS_SIZE = 1;
134:
135: /**************************************************************************
136: * Get accessors for testing bits in the status field.
137: **************************************************************************
138: */
139:
140: /**
141: Get the status of the field
142:
143: <BR> MT - single thread required
144: */
145: public static final boolean isNull(int status) {
146: return ((status & FIELD_NULL) == FIELD_NULL);
147: }
148:
149: public static final boolean isOverflow(int status) {
150: return ((status & FIELD_OVERFLOW) == FIELD_OVERFLOW);
151: }
152:
153: public static final boolean isNonexistent(int status) {
154: return ((status & FIELD_NONEXISTENT) == FIELD_NONEXISTENT);
155: }
156:
157: public static final boolean isExtensible(int status) {
158: return ((status & FIELD_EXTENSIBLE) == FIELD_EXTENSIBLE);
159: }
160:
161: public static final boolean isNullorNonExistent(int status) {
162: // just need to check whether null bit is set.
163: // return ((status & FIELD_NONEXISTENT) == FIELD_NONEXISTENT);
164: return ((status & FIELD_NULL) != 0);
165: }
166:
167: public static final boolean isTagged(int status) {
168: // if (SanityManager.DEBUG)
169: // SanityManager.ASSERT(isExtensible(status), "a field cannot be tagged if it is not extensible");
170: return ((status & FIELD_TAGGED) == FIELD_TAGGED);
171: }
172:
173: public static final boolean isFixed(int status) {
174: return ((status & FIELD_FIXED) == FIELD_FIXED);
175: }
176:
177: public static final boolean isNullable(int status) {
178: return ((status & FIELD_NOT_NULLABLE) == 0);
179: }
180:
181: public static final int size(int status, int fieldDataLength,
182: int fieldDataSize) {
183:
184: if ((status & (FIELD_NULL | FIELD_FIXED)) == 0) {
185: // usual case - not-null, not-fixed
186:
187: // WARNING - the following code hand inlined from
188: // CompressedNumber for performance.
189: //
190: // return(CompressedNumber.sizeInt(fieldDataLength) + 1);
191: //
192:
193: if (fieldDataLength <= CompressedNumber.MAX_COMPRESSED_INT_ONE_BYTE) {
194: // compressed form is 1 byte
195: return (2);
196: } else if (fieldDataLength <= CompressedNumber.MAX_COMPRESSED_INT_TWO_BYTES) {
197: // compressed form is 2 bytes
198: return (3);
199: } else {
200: // compressed form is 4 bytes
201: return (5);
202: }
203: } else if ((status & FIELD_NULL) != 0) {
204: // field is null
205:
206: return (1);
207: } else {
208: // fixed length field
209:
210: return ((fieldDataSize > 2) ? 5 : 3);
211: }
212: }
213:
214: /**************************************************************************
215: * Set accessors for setting bits in the status field.
216: **************************************************************************
217: */
218:
219: public final static int setInitial() {
220: return FIELD_INITIAL;
221: }
222:
223: public final static int setNull(int status, boolean isNull) {
224: if (isNull)
225: status |= FIELD_NULL;
226: else
227: status &= ~FIELD_NULL;
228: return status;
229: }
230:
231: public final static int setOverflow(int status, boolean isOverflow) {
232: if (isOverflow)
233: status |= FIELD_OVERFLOW;
234: else
235: status &= ~FIELD_OVERFLOW;
236: return status;
237: }
238:
239: public final static int setNonexistent(int status) {
240: status |= FIELD_NONEXISTENT;
241: return status;
242: }
243:
244: public final static int setExtensible(int status,
245: boolean isExtensible) {
246: if (isExtensible)
247: status |= FIELD_EXTENSIBLE;
248: else
249: status &= ~FIELD_EXTENSIBLE;
250: return status;
251: }
252:
253: public final static int setTagged(int status, boolean isTagged) {
254:
255: if (SanityManager.DEBUG)
256: SanityManager
257: .ASSERT(isExtensible(status),
258: "a field cannot be set to tagged if it is not extensible");
259:
260: if (isTagged)
261: status |= FIELD_TAGGED;
262: else
263: status &= ~FIELD_TAGGED;
264: return status;
265: }
266:
267: public final static int setFixed(int status, boolean isFixed) {
268: if (isFixed)
269: status |= FIELD_FIXED;
270: else
271: status &= ~FIELD_FIXED;
272: return status;
273: }
274:
275: /**************************************************************************
276: * routines used to write a field header to a OutputStream
277: **************************************************************************
278: */
279:
280: /**
281: write out the field status and field data Length
282:
283: @exception IOException Thrown by potential I/O errors while writing
284: field header.
285: */
286: public static final int write(OutputStream out, int status,
287: int fieldDataLength, int fieldDataSize) throws IOException {
288: int len = 1;
289:
290: out.write(status);
291:
292: if (isNull(status))
293: return len;
294:
295: if (isFixed(status)) {
296: // if the field header is for log, we write it in compressed format,
297: // then we pad the field, so the total length is fixed.
298: if (fieldDataSize > 2) {
299: int diffLen = fieldDataSize
300: - CompressedNumber.writeInt(out,
301: fieldDataLength);
302:
303: for (int i = diffLen; i > 0; i--)
304: out.write(0);
305: len += fieldDataSize; // size of an int - 4 bytes
306: } else {
307: // write the int out as a short
308: out.write((fieldDataLength >>> 8) & 0xFF);
309: out.write((fieldDataLength >>> 0) & 0xFF);
310: len += 2; // size of a short - 2 bytes
311: }
312:
313: // NOTE: fixed version is used for logs only,
314: // the overflow information is stored at the end of the optional
315: // data, not in the field headers.
316: // That's why we are not writing overflow info here.
317:
318: } else {
319: // if we are writing the fieldHeader for the page,
320: // we write in compressed format
321:
322: len += CompressedNumber.writeInt(out, fieldDataLength);
323: }
324:
325: return len;
326: }
327:
328: /**************************************************************************
329: * routines used to read a field header from an ObjectInput stream, array
330: **************************************************************************
331: */
332:
333: /**
334: read the field status
335:
336: @exception IOException Thrown by potential I/O errors while reading
337: field header.
338: */
339: public static final int readStatus(ObjectInput in)
340: throws IOException {
341: int status;
342:
343: if ((status = in.read()) >= 0)
344: return status;
345: else
346: throw new EOFException();
347: }
348:
349: public static final int readStatus(byte[] page, int offset) {
350: return (page[offset]);
351: }
352:
353: /**
354: * read the length of the field and hdr.
355: * <p>
356: * Optimized routine used to skip a field on a page. It returns the
357: * total length of the field including the header portion. It operates
358: * directly on the array and does no checking of it's own for limits on
359: * the array length, so an array out of bounds exception may be thrown -
360: * the routine is meant to be used to read a field from a page so this
361: * should not happen.
362: * <p>
363: *
364: * @return The length of the field on the page, including it's header.
365: *
366: * @param data the array where the field is.
367: * @param offset the offset in the array where the field begin, ie.
368: * the status byte is at data[offset].
369: *
370: * @exception StandardException Standard exception policy.
371: **/
372: public static final int readTotalFieldLength(byte[] data, int offset)
373: throws IOException {
374: if (SanityManager.DEBUG) {
375: // this routine is meant to be called on the page, and FIXED fields
376: // are only used in the log.
377:
378: if (isFixed(data[offset]))
379: SanityManager
380: .THROWASSERT("routine does not handle FIXED.");
381: }
382:
383: if (((data[offset++]) & FIELD_NULL) != FIELD_NULL) {
384: int value = data[offset];
385:
386: if ((value & ~0x3f) == 0) {
387: // length is stored in this byte, we also know that the 0x80 bit
388: // was not set, so no need to mask off the sign extension from
389: // the byte to int conversion.
390:
391: // account for 1 byte stored length of field + 1 for status.
392: return (value + 2);
393: } else if ((value & 0x80) == 0) {
394: // length stored in 2 bytes. only use low 6 bits from 1st byte.
395:
396: if (SanityManager.DEBUG) {
397: SanityManager.ASSERT((value & 0x40) == 0x40);
398: }
399:
400: // top 8 bits of 2 byte length is stored in this byte, we also
401: // know that the 0x80 bit was not set, so no need to mask off
402: // the sign extension from the 1st byte to int conversion. Need
403: // to mask the byte in data[offset + 1] to account for possible
404: // sign extension.
405:
406: // add 3 to account for 2 byte length + 1 for status
407:
408: return ((((value & 0x3f) << 8) | (data[offset + 1] & 0xff)) + 3);
409: } else {
410: // length stored in 4 bytes. only use low 7 bits from 1st byte.
411:
412: if (SanityManager.DEBUG) {
413: SanityManager.ASSERT((value & 0x80) == 0x80);
414: }
415:
416: // top 8 bits of 4 byte length is stored in this byte, we also
417: // know that the 0x80 bit was set, so need to mask off the
418: // sign extension from the 1st byte to int conversion. Need to
419: // mask the bytes from the next 3 bytes data[offset + 1,2,3] to
420: // account for possible sign extension.
421:
422: // add 5 to account for 4 byte length + 1 added to all returns
423: return ((((value & 0x7f) << 24)
424: | ((data[offset + 1] & 0xff) << 16)
425: | ((data[offset + 2] & 0xff) << 8) | (data[offset + 3] & 0xff)) + 5);
426: }
427: } else {
428: return (1);
429: }
430: }
431:
432: public static final int readFieldLengthAndSetStreamPosition(
433: byte[] data, int offset, int status, int fieldDataSize,
434: ArrayInputStream ais) throws IOException {
435: if ((status & (FIELD_NULL | FIELD_FIXED)) == 0) {
436: // usual case-not null, not fixed. Length stored as compressed int.
437: // return(CompressedNumber.readInt(in));
438:
439: int value = data[offset++];
440:
441: if ((value & ~0x3f) == 0) {
442: // usual case.
443:
444: // length is stored in this byte, we also know that the 0x80 bit
445: // was not set, so no need to mask off the sign extension from
446: // the byte to int conversion.
447:
448: // nothing to do, value already has int to return.
449:
450: } else if ((value & 0x80) == 0) {
451: // length is stored in 2 bytes. use low 6 bits from 1st byte.
452:
453: if (SanityManager.DEBUG) {
454: SanityManager.ASSERT((value & 0x40) == 0x40);
455: }
456:
457: // top 8 bits of 2 byte length is stored in this byte, we also
458: // know that the 0x80 bit was not set, so no need to mask off
459: // the sign extension from the 1st byte to int conversion.
460: // Need to mask the byte in data[offset + 1] to account for
461: // possible sign extension.
462:
463: value = (((value & 0x3f) << 8) | (data[offset++] & 0xff));
464:
465: } else {
466: // length is stored in 4 bytes. only low 7 bits from 1st byte.
467:
468: if (SanityManager.DEBUG) {
469: SanityManager.ASSERT((value & 0x80) == 0x80);
470: }
471:
472: // top 8 bits of 4 byte length is stored in this byte, we also
473: // know that the 0x80 bit was set, so need to mask off the
474: // sign extension from the 1st byte to int conversion. Need to
475: // mask the bytes from the next 3 bytes data[offset + 1,2,3] to
476: // account for possible sign extension.
477:
478: // add 5 to account for 4 byte length + 1 added to all returns
479: value = (((value & 0x7f) << 24)
480: | ((data[offset++] & 0xff) << 16)
481: | ((data[offset++] & 0xff) << 8) | (data[offset++] & 0xff));
482: }
483:
484: ais.setPosition(offset);
485:
486: return (value);
487: } else if ((status & FIELD_NULL) != 0) {
488: ais.setPosition(offset);
489: return (0);
490: } else {
491: int fieldDataLength;
492:
493: // field data length is in a fixed size field, not compressed.
494:
495: if (fieldDataSize <= 2) {
496: // read it in as short, because it was written out as short
497: fieldDataLength = ((data[offset++] & 0xff) << 8)
498: | (data[offset++] & 0xff);
499: } else {
500: // fieldDataLength = CompressedNumber.readInt(in);
501:
502: fieldDataLength = data[offset];
503:
504: if ((fieldDataLength & ~0x3f) == 0) {
505: // usual case.
506:
507: // length is stored in this byte, we also know that the 0x80
508: // bit was not set, so no need to mask off the sign
509: // extension from the byte to int conversion.
510:
511: // nothing to do, fieldDataLength already has int to return.
512:
513: } else if ((fieldDataLength & 0x80) == 0) {
514: // len is stored in 2 bytes. use low 6 bits from 1st byte.
515:
516: if (SanityManager.DEBUG) {
517: SanityManager
518: .ASSERT((fieldDataLength & 0x40) == 0x40);
519: }
520:
521: // top 8 bits of 2 byte length is stored in this byte, we
522: // also know that the 0x80 bit was not set, so no need to
523: // mask off the sign extension from the 1st byte to int
524: // conversion. Need to mask the byte in data[offset + 1] to
525: // account for possible sign extension.
526:
527: fieldDataLength = (((fieldDataLength & 0x3f) << 8) | (data[offset + 1] & 0xff));
528:
529: } else {
530: // len is stored in 4 bytes. only low 7 bits from 1st byte.
531:
532: if (SanityManager.DEBUG) {
533: SanityManager
534: .ASSERT((fieldDataLength & 0x80) == 0x80);
535: }
536:
537: // top 8 bits of 4 byte length is stored in this byte, we
538: // also know that the 0x80 bit was set, so need to mask off
539: // the sign extension from the 1st byte to int conversion.
540: // Need to mask the bytes from the next 3 bytes
541: // data[offset + 1,2,3] to account for possible sign
542: // extension.
543:
544: fieldDataLength = (((fieldDataLength & 0x7f) << 24)
545: | ((data[offset + 1] & 0xff) << 16)
546: | ((data[offset + 2] & 0xff) << 8) | (data[offset + 3] & 0xff));
547: }
548:
549: offset = offset + fieldDataSize;
550: }
551:
552: ais.setPosition(offset);
553: return (fieldDataLength);
554: }
555:
556: }
557:
558: /**
559: read the field data length
560:
561: @exception IOException Thrown by potential I/O errors while reading
562: field header.
563: */
564: public static final int readFieldDataLength(ObjectInput in,
565: int status, int fieldDataSize) throws IOException {
566:
567: if ((status & (FIELD_NULL | FIELD_FIXED)) == 0) {
568: // usual case-not null, not fixed. Length stored as compressed int.
569: return (CompressedNumber.readInt(in));
570: } else if ((status & FIELD_NULL) != 0) {
571: // field is null or non-existent.
572: return (0);
573: } else {
574: int fieldDataLength;
575:
576: // field data length is in a fixed size field, not compressed.
577:
578: if (fieldDataSize <= 2) {
579: // read it in as short, because it was written out as short
580: int ch1 = in.read();
581: int ch2 = in.read();
582: if ((ch1 | ch2) < 0)
583: throw new EOFException();
584:
585: fieldDataLength = ((ch1 << 8) + (ch2 << 0));
586: } else {
587: fieldDataLength = CompressedNumber.readInt(in);
588:
589: int diffLen = fieldDataSize
590: - CompressedNumber.sizeInt(fieldDataLength);
591:
592: if (diffLen != 0)
593: in.skipBytes(diffLen);
594: }
595:
596: return (fieldDataLength);
597: }
598: }
599:
600: public static String toDebugString(int status) {
601: if (SanityManager.DEBUG) {
602: StringBuffer str = new StringBuffer(100);
603: if (isNull(status))
604: str.append("Null ");
605: if (isOverflow(status))
606: str.append("Overflow ");
607: if (isNonexistent(status))
608: str.append("Nonexistent ");
609: if (isExtensible(status))
610: str.append("Extensible ");
611: if (isTagged(status))
612: str.append("Tagged ");
613: if (isFixed(status))
614: str.append("Fixed ");
615: if (isNullable(status))
616: str.append("Nullable ");
617: if (str.length() == 0)
618: str.append("INITIAL ");
619:
620: return str.toString();
621: }
622: return null;
623: }
624: }
|