001: /*
002:
003: Derby - Class org.apache.derby.iapi.types.SQLBinary
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:
022: package org.apache.derby.iapi.types;
023:
024: import org.apache.derby.iapi.reference.SQLState;
025:
026: import org.apache.derby.iapi.services.io.ArrayInputStream;
027: import org.apache.derby.iapi.services.io.FormatableBitSet;
028: import org.apache.derby.iapi.services.io.NewByteArrayInputStream;
029:
030: import org.apache.derby.iapi.types.DataTypeDescriptor;
031: import org.apache.derby.iapi.types.DataValueDescriptor;
032: import org.apache.derby.iapi.types.TypeId;
033: import org.apache.derby.iapi.types.BitDataValue;
034: import org.apache.derby.iapi.types.DataValueDescriptor;
035: import org.apache.derby.iapi.types.ConcatableDataValue;
036: import org.apache.derby.iapi.types.VariableSizeDataValue;
037: import org.apache.derby.iapi.error.StandardException;
038:
039: import org.apache.derby.iapi.services.io.FormatIdUtil;
040: import org.apache.derby.iapi.services.io.StoredFormatIds;
041: import org.apache.derby.iapi.services.io.StreamStorable;
042: import org.apache.derby.iapi.services.io.FormatIdInputStream;
043:
044: import org.apache.derby.iapi.services.sanity.SanityManager;
045:
046: import org.apache.derby.iapi.types.BooleanDataValue;
047: import org.apache.derby.iapi.types.StringDataValue;
048: import org.apache.derby.iapi.types.NumberDataValue;
049:
050: import org.apache.derby.iapi.services.cache.ClassSize;
051: import org.apache.derby.iapi.util.StringUtil;
052:
053: import org.apache.derby.iapi.types.SQLInteger;
054:
055: import java.io.ObjectOutput;
056: import java.io.ObjectInput;
057: import java.io.IOException;
058: import java.io.InputStream;
059:
060: import java.sql.ResultSet;
061: import java.sql.SQLException;
062: import java.sql.PreparedStatement;
063:
064: /**
065: * SQLBinary is the abstract class for the binary datatypes.
066: * <UL>
067: * <LI> CHAR FOR BIT DATA
068: * <LI> VARCHAR FOR BIT DATA
069: * <LI> LONG VARCHAR
070: * <LI> BLOB
071: * </UL>
072:
073: <P>
074: Format : <encoded length><raw data>
075: <BR>
076: Length is encoded to support Cloudscape 5.x databases where the length was stored as the number of bits.
077: The first bit of the first byte indicates if the format is an old (Cloudscape 5.x) style or a new Derby style.
078: Derby then uses the next two bits to indicate how the length is encoded.
079: <BR>
080: <encoded length> is one of N styles.
081: <UL>
082: <LI> (5.x format zero) 4 byte Java format integer value 0 - either <raw data> is 0 bytes/bits or an unknown number of bytes.
083: <LI> (5.x format bits) 4 byte Java format integer value >0 (positive) - number of bits in <raw data>, number of bytes in <raw data>
084: is the minimum number of bytes required to store the number of bits.
085: <LI> (Derby format) 1 byte encoded length (0 <= L <= 31) - number of bytes of <raw data> - encoded = 0x80 & L
086: <LI> (Derby format) 3 byte encoded length (32 <= L < 64k) - number of bytes of <raw data> - encoded = 0xA0 <L as Java format unsigned short>
087: <LI> (Derby format) 5 byte encoded length (64k <= L < 2G) - number of bytes of <raw data> - encoded = 0xC0 <L as Java format integer>
088: <LI> (future) to be determined L >= 2G - encoded 0xE0 <encoding of L to be determined>
089: (0xE0 is an esacape to allow any number of arbitary encodings in the future).
090: </UL>
091: <BR>
092: When the value was written from a byte array the Derby encoded byte
093: length format was always used from Derby 10.0 onwards (ie. all open
094: source versions).
095: <BR>
096: When the value was written from a stream (e.g. PreparedStatement.setBinaryStream)
097: then the Cloudscape '5.x format zero' was used by 10.0 and 10.1.
098: The was due to the class RawToBinaryFormatStream always writing
099: four zero bytes for the length before the data.
100: <BR>
101: The Cloudscape '5.x format bits' format I think was never used by Derby.
102: */
103: abstract class SQLBinary extends DataType implements BitDataValue {
104:
105: static final byte PAD = (byte) 0x20;
106:
107: private static final int BASE_MEMORY_USAGE = ClassSize
108: .estimateBaseFromCatalog(SQLBinary.class);
109:
110: public int estimateMemoryUsage() {
111: if (dataValue == null) {
112: if (streamValueLength >= 0) {
113: return BASE_MEMORY_USAGE + streamValueLength;
114: } else {
115: return getMaxMemoryUsage();
116: }
117: } else {
118: return BASE_MEMORY_USAGE + dataValue.length;
119: }
120: } // end of estimateMemoryUsage
121:
122: /**
123: * Return max memory usage for a SQL Binary
124: */
125: abstract int getMaxMemoryUsage();
126:
127: /*
128: * object state
129: */
130: byte[] dataValue;
131:
132: /**
133: * Value as a stream, this stream represents the on-disk
134: * format of the value. That is it has length information
135: * encoded in the first fe bytes.
136: */
137: InputStream stream;
138:
139: /**
140: Length of the value in bytes when this value
141: is set as a stream. Represents the length of the
142: value itself and not the length of the stream
143: which contains this length encoded as the first
144: few bytes. If the value of the stream is unknown
145: then this will be set to -1. If this value is
146: not set as a stream then this value should be ignored.
147: */
148: int streamValueLength;
149:
150: /**
151: no-arg constructor, required by Formattable.
152: */
153: SQLBinary() {
154: }
155:
156: SQLBinary(byte[] val) {
157: dataValue = val;
158: }
159:
160: public final void setValue(byte[] theValue) {
161: dataValue = theValue;
162: stream = null;
163: streamValueLength = -1;
164: }
165:
166: /**
167: * Used by JDBC -- string should not contain
168: * SQL92 formatting.
169: *
170: * @exception StandardException Thrown on error
171: */
172: public final String getString() throws StandardException {
173: if (getValue() == null)
174: return null;
175: else if (dataValue.length * 2 < 0) //if converted to hex, length exceeds max int
176: {
177: throw StandardException.newException(
178: SQLState.LANG_STRING_TRUNCATION, getTypeName(), "",
179: String.valueOf(Integer.MAX_VALUE));
180: } else {
181: return org.apache.derby.iapi.util.StringUtil.toHexString(
182: dataValue, 0, dataValue.length);
183: }
184: }
185:
186: /**
187: * @exception StandardException Thrown on error
188: */
189: public final InputStream getStream() {
190: return (stream);
191: }
192:
193: /**
194: *
195: * @exception StandardException Thrown on error
196: */
197: public final byte[] getBytes() throws StandardException {
198: return getValue();
199: }
200:
201: byte[] getValue() throws StandardException {
202: try {
203: if ((dataValue == null) && (stream != null)) {
204:
205: if (stream instanceof FormatIdInputStream) {
206: readExternal((FormatIdInputStream) stream);
207: } else {
208: readExternal(new FormatIdInputStream(stream));
209: }
210: stream = null;
211: streamValueLength = -1;
212:
213: }
214: } catch (IOException ioe) {
215: throw StandardException.newException(
216: SQLState.LANG_STREAMING_COLUMN_I_O_EXCEPTION, ioe,
217: getTypeName());
218: }
219: return dataValue;
220: }
221:
222: /**
223: * length in bytes
224: *
225: * @exception StandardException Thrown on error
226: */
227: public final int getLength() throws StandardException {
228: if (stream != null) {
229:
230: if (streamValueLength != -1)
231: return streamValueLength;
232: }
233:
234: return (getBytes() == null) ? 0 : getBytes().length;
235: }
236:
237: /*
238: * Storable interface, implies Externalizable, TypedFormat
239: */
240:
241: /**
242: * see if the Bit value is null.
243: * @see org.apache.derby.iapi.services.io.Storable#isNull
244: */
245: public final boolean isNull() {
246: return (dataValue == null) && (stream == null);
247: }
248:
249: /**
250: Write the value out from the byte array (not called if null)
251: using the 8.1 encoding.
252:
253: * @exception IOException io exception
254: */
255: public final void writeExternal(ObjectOutput out)
256: throws IOException {
257:
258: int len = dataValue.length;
259: if (len <= 31) {
260: out.write((byte) (0x80 | (len & 0xff)));
261: } else if (len <= 0xFFFF) {
262: out.write((byte) 0xA0);
263: out.writeShort((short) len);
264: } else {
265: out.write((byte) 0xC0);
266: out.writeInt(len);
267:
268: }
269: out.write(dataValue, 0, dataValue.length);
270: }
271:
272: /**
273: * delegated to bit
274: *
275: * @exception IOException io exception
276: * @exception ClassNotFoundException class not found
277: */
278: public final void readExternal(ObjectInput in) throws IOException {
279: // need to clear stream first, in case this object is reused, and
280: // stream is set by previous use. Track 3794.
281: stream = null;
282: streamValueLength = -1;
283:
284: int len = SQLBinary.readBinaryLength(in);
285:
286: if (len != 0) {
287: dataValue = new byte[len];
288: in.readFully(dataValue);
289: } else {
290: readFromStream((InputStream) in);
291: }
292: }
293:
294: public final void readExternalFromArray(ArrayInputStream in)
295: throws IOException {
296: // need to clear stream first, in case this object is reused, and
297: // stream is set by previous use. Track 3794.
298: stream = null;
299: streamValueLength = -1;
300:
301: int len = SQLBinary.readBinaryLength(in);
302:
303: if (len != 0) {
304: dataValue = new byte[len];
305: in.readFully(dataValue);
306: } else {
307: readFromStream(in);
308: }
309: }
310:
311: /**
312: * Read the encoded length of the value from the on-disk format.
313: *
314: * @see SQLBinary
315: */
316: private static int readBinaryLength(ObjectInput in)
317: throws IOException {
318:
319: int bl = in.read();
320: if (bl == -1)
321: throw new java.io.EOFException();
322:
323: byte li = (byte) bl;
324:
325: int len;
326: if ((li & ((byte) 0x80)) != 0) {
327: if (li == ((byte) 0xC0)) {
328: len = in.readInt();
329: } else if (li == ((byte) 0xA0)) {
330: len = in.readUnsignedShort();
331: } else {
332: len = li & 0x1F;
333: }
334: } else {
335:
336: // old length in bits
337: int v2 = in.read();
338: int v3 = in.read();
339: int v4 = in.read();
340: if (v2 == -1 || v3 == -1 || v4 == -1)
341: throw new java.io.EOFException();
342: int lenInBits = (((bl & 0xff) << 24) | ((v2 & 0xff) << 16)
343: | ((v3 & 0xff) << 8) | (v4 & 0xff));
344:
345: len = lenInBits / 8;
346: if ((lenInBits % 8) != 0)
347: len++;
348: }
349: return len;
350: }
351:
352: /**
353: * Read the value from an input stream. The length
354: * encoded in the input stream has already been read
355: * and determined to be unknown.
356: */
357: private void readFromStream(InputStream in) throws IOException {
358:
359: dataValue = null; // allow gc of the old value before the new.
360: byte[] tmpData = new byte[32 * 1024];
361:
362: int off = 0;
363: for (;;) {
364:
365: int len = in.read(tmpData, off, tmpData.length - off);
366: if (len == -1)
367: break;
368: off += len;
369:
370: int available = Math.max(1, in.available());
371: int extraSpace = available - (tmpData.length - off);
372: if (extraSpace > 0) {
373: // need to grow the array
374: int size = tmpData.length * 2;
375: if (extraSpace > tmpData.length)
376: size += extraSpace;
377:
378: byte[] grow = new byte[size];
379: System.arraycopy(tmpData, 0, grow, 0, off);
380: tmpData = grow;
381: }
382: }
383:
384: dataValue = new byte[off];
385: System.arraycopy(tmpData, 0, dataValue, 0, off);
386: }
387:
388: /**
389: * @see org.apache.derby.iapi.services.io.Storable#restoreToNull
390: */
391: public final void restoreToNull() {
392: dataValue = null;
393: stream = null;
394: streamValueLength = -1;
395: }
396:
397: /**
398: @exception StandardException thrown on error
399: */
400: public final boolean compare(int op, DataValueDescriptor other,
401: boolean orderedNulls, boolean unknownRV)
402: throws StandardException {
403: if (!orderedNulls) // nulls are unordered
404: {
405: if (SanityManager.DEBUG) {
406: int otherTypeFormatId = other.getTypeFormatId();
407: if (!((StoredFormatIds.SQL_BIT_ID == otherTypeFormatId)
408: || (StoredFormatIds.SQL_VARBIT_ID == otherTypeFormatId)
409: || (StoredFormatIds.SQL_LONGVARBIT_ID == otherTypeFormatId)
410:
411: || (StoredFormatIds.SQL_CHAR_ID == otherTypeFormatId)
412: || (StoredFormatIds.SQL_VARCHAR_ID == otherTypeFormatId)
413: || (StoredFormatIds.SQL_LONGVARCHAR_ID == otherTypeFormatId)
414:
415: || ((StoredFormatIds.SQL_BLOB_ID == otherTypeFormatId) && (StoredFormatIds.SQL_BLOB_ID == getTypeFormatId()))))
416: SanityManager.THROWASSERT("Some fool passed in a "
417: + other.getClass().getName() + ", "
418: + otherTypeFormatId
419: + " to SQLBinary.compare()");
420: }
421: String otherString = other.getString();
422: if (this .getString() == null || otherString == null)
423: return unknownRV;
424: }
425: /* Do the comparison */
426: return super .compare(op, other, orderedNulls, unknownRV);
427: }
428:
429: /**
430: @exception StandardException thrown on error
431: */
432: public final int compare(DataValueDescriptor other)
433: throws StandardException {
434:
435: /* Use compare method from dominant type, negating result
436: * to reflect flipping of sides.
437: */
438: if (typePrecedence() < other.typePrecedence()) {
439: return -(other.compare(this ));
440: }
441:
442: /*
443: ** By convention, nulls sort High, and null == null
444: */
445: if (this .isNull() || other.isNull()) {
446: if (!isNull())
447: return -1;
448: if (!other.isNull())
449: return 1;
450: return 0; // both null
451: }
452:
453: return SQLBinary.compare(getBytes(), other.getBytes());
454: }
455:
456: /*
457: * CloneableObject interface
458: */
459:
460: /** From CloneableObject
461: * Shallow clone a StreamStorable without objectifying. This is used to avoid
462: * unnecessary objectifying of a stream object. The only difference of this method
463: * from getClone is this method does not objectify a stream. beetle 4896
464: */
465: public final Object cloneObject() {
466: if (stream == null)
467: return getClone();
468: SQLBinary self = (SQLBinary) getNewNull();
469: self.setValue(stream, streamValueLength);
470: return self;
471: }
472:
473: /*
474: * DataValueDescriptor interface
475: */
476:
477: /** @see DataValueDescriptor#getClone */
478: public final DataValueDescriptor getClone() {
479: try {
480: DataValueDescriptor cloneDVD = getNewNull();
481: cloneDVD.setValue(getValue());
482: return cloneDVD;
483: } catch (StandardException se) {
484: if (SanityManager.DEBUG)
485: SanityManager.THROWASSERT("Unexpected exception " + se);
486: return null;
487: }
488: }
489:
490: /*
491: * DataValueDescriptor interface
492: */
493:
494: /*
495: * StreamStorable interface :
496: */
497: public final InputStream returnStream() {
498: return stream;
499: }
500:
501: /**
502: * Set me to the value represented by this stream.
503: * The format of the stream is the on-disk format
504: * described in this class's javadoc. That is the
505: * length is encoded in the first few bytes of the
506: * stream.
507: */
508: public final void setStream(InputStream newStream) {
509: this .dataValue = null;
510: this .stream = newStream;
511: streamValueLength = -1;
512: }
513:
514: public final void loadStream() throws StandardException {
515: getValue();
516: }
517:
518: /*
519: * class interface
520: */
521:
522: boolean objectNull(Object o) {
523: if (o == null) {
524: setToNull();
525: return true;
526: }
527: return false;
528: }
529:
530: /**
531: * Set the value from the stream which is in the on-disk format.
532: * @param theStream On disk format of the stream
533: * @param valueLength length of the logical value in bytes.
534: */
535: public final void setValue(InputStream theStream, int valueLength) {
536: dataValue = null;
537: stream = theStream;
538: this .streamValueLength = valueLength;
539: }
540:
541: protected final void setFrom(DataValueDescriptor theValue)
542: throws StandardException {
543:
544: if (theValue instanceof SQLBinary) {
545: SQLBinary theValueBinary = (SQLBinary) theValue;
546: dataValue = theValueBinary.dataValue;
547: stream = theValueBinary.stream;
548: streamValueLength = theValueBinary.streamValueLength;
549: } else {
550: setValue(theValue.getBytes());
551: }
552: }
553:
554: /*
555: ** SQL Operators
556: */
557:
558: /**
559: * The = operator as called from the language module, as opposed to
560: * the storage module.
561: *
562: * @param left The value on the left side of the =
563: * @param right The value on the right side of the =
564: * is not.
565: * @return A SQL boolean value telling whether the two parameters are equal
566: *
567: * @exception StandardException Thrown on error
568: */
569:
570: public final BooleanDataValue equals(DataValueDescriptor left,
571: DataValueDescriptor right) throws StandardException {
572: boolean isEqual;
573:
574: if (left.isNull() || right.isNull()) {
575: isEqual = false;
576: } else {
577: isEqual = SQLBinary.compare(left.getBytes(), right
578: .getBytes()) == 0;
579: }
580:
581: return SQLBoolean.truthValue(left, right, isEqual);
582: }
583:
584: /**
585: * The <> operator as called from the language module, as opposed to
586: * the storage module.
587: *
588: * @param left The value on the left side of the <>
589: * @param right The value on the right side of the <>
590: *
591: * @return A SQL boolean value telling whether the two parameters
592: * are not equal
593: *
594: * @exception StandardException Thrown on error
595: */
596:
597: public final BooleanDataValue notEquals(DataValueDescriptor left,
598: DataValueDescriptor right) throws StandardException {
599: boolean isNotEqual;
600:
601: if (left.isNull() || right.isNull()) {
602: isNotEqual = false;
603: } else {
604: isNotEqual = SQLBinary.compare(left.getBytes(), right
605: .getBytes()) != 0;
606: }
607:
608: return SQLBoolean.truthValue(left, right, isNotEqual);
609: }
610:
611: /**
612: * The < operator as called from the language module, as opposed to
613: * the storage module.
614: *
615: * @param left The value on the left side of the <
616: * @param right The value on the right side of the <
617: *
618: * @return A SQL boolean value telling whether the first operand is
619: * less than the second operand
620: *
621: * @exception StandardException Thrown on error
622: */
623:
624: public final BooleanDataValue lessThan(DataValueDescriptor left,
625: DataValueDescriptor right) throws StandardException {
626: boolean isLessThan;
627:
628: if (left.isNull() || right.isNull()) {
629: isLessThan = false;
630: } else {
631: isLessThan = SQLBinary.compare(left.getBytes(), right
632: .getBytes()) < 0;
633: }
634:
635: return SQLBoolean.truthValue(left, right, isLessThan);
636: }
637:
638: /**
639: * The > operator as called from the language module, as opposed to
640: * the storage module.
641: *
642: * @param left The value on the left side of the >
643: * @param right The value on the right side of the >
644: *
645: * @return A SQL boolean value telling whether the first operand is
646: * greater than the second operand
647: *
648: * @exception StandardException Thrown on error
649: */
650:
651: public final BooleanDataValue greaterThan(DataValueDescriptor left,
652: DataValueDescriptor right) throws StandardException {
653: boolean isGreaterThan = false;
654:
655: if (left.isNull() || right.isNull()) {
656: isGreaterThan = false;
657: } else {
658: isGreaterThan = SQLBinary.compare(left.getBytes(), right
659: .getBytes()) > 0;
660: }
661:
662: return SQLBoolean.truthValue(left, right, isGreaterThan);
663: }
664:
665: /**
666: * The <= operator as called from the language module, as opposed to
667: * the storage module.
668: *
669: * @param left The value on the left side of the <=
670: * @param right The value on the right side of the <=
671: *
672: * @return A SQL boolean value telling whether the first operand is
673: * less than or equal to the second operand
674: *
675: * @exception StandardException Thrown on error
676: */
677:
678: public final BooleanDataValue lessOrEquals(
679: DataValueDescriptor left, DataValueDescriptor right)
680: throws StandardException {
681: boolean isLessEquals = false;
682:
683: if (left.isNull() || right.isNull()) {
684: isLessEquals = false;
685: } else {
686: isLessEquals = SQLBinary.compare(left.getBytes(), right
687: .getBytes()) <= 0;
688: }
689:
690: return SQLBoolean.truthValue(left, right, isLessEquals);
691: }
692:
693: /**
694: * The >= operator as called from the language module, as opposed to
695: * the storage module.
696: *
697: * @param left The value on the left side of the >=
698: * @param right The value on the right side of the >=
699: *
700: * @return A SQL boolean value telling whether the first operand is
701: * greater than or equal to the second operand
702: *
703: * @exception StandardException Thrown on error
704: */
705:
706: public final BooleanDataValue greaterOrEquals(
707: DataValueDescriptor left, DataValueDescriptor right)
708: throws StandardException {
709: boolean isGreaterEquals = false;
710:
711: if (left.isNull() || right.isNull()) {
712: isGreaterEquals = false;
713: } else {
714: isGreaterEquals = SQLBinary.compare(left.getBytes(), right
715: .getBytes()) >= 0;
716: }
717:
718: return SQLBoolean.truthValue(left, right, isGreaterEquals);
719: }
720:
721: /**
722: *
723: * This method implements the char_length function for bit.
724: *
725: * @param result The result of a previous call to this method, null
726: * if not called yet
727: *
728: * @return A SQLInteger containing the length of the char value
729: *
730: * @exception StandardException Thrown on error
731: *
732: * @see ConcatableDataValue#charLength
733: */
734:
735: public final NumberDataValue charLength(NumberDataValue result)
736: throws StandardException {
737: if (result == null) {
738: result = new SQLInteger();
739: }
740:
741: if (this .isNull()) {
742: result.setToNull();
743: return result;
744: }
745:
746: result.setValue(getValue().length);
747: return result;
748: }
749:
750: /**
751: * @see BitDataValue#concatenate
752: *
753: * @exception StandardException Thrown on error
754: */
755: public final BitDataValue concatenate(BitDataValue left,
756: BitDataValue right, BitDataValue result)
757: throws StandardException {
758: if (left.isNull() || right.isNull()) {
759: result.setToNull();
760: return result;
761: }
762:
763: byte[] leftData = left.getBytes();
764: byte[] rightData = right.getBytes();
765:
766: byte[] concatData = new byte[leftData.length + rightData.length];
767:
768: System.arraycopy(leftData, 0, concatData, 0, leftData.length);
769: System.arraycopy(rightData, 0, concatData, leftData.length,
770: rightData.length);
771:
772: result.setValue(concatData);
773: return result;
774: }
775:
776: /**
777: * The SQL substr() function.
778: *
779: * @param start Start of substr
780: * @param length Length of substr
781: * @param result The result of a previous call to this method,
782: * null if not called yet.
783: * @param maxLen Maximum length of the result
784: *
785: * @return A ConcatableDataValue containing the result of the substr()
786: *
787: * @exception StandardException Thrown on error
788: */
789: public final ConcatableDataValue substring(NumberDataValue start,
790: NumberDataValue length, ConcatableDataValue result,
791: int maxLen) throws StandardException {
792: int startInt;
793: int lengthInt;
794: BitDataValue varbitResult;
795:
796: if (result == null) {
797: result = new SQLVarbit();
798: }
799:
800: varbitResult = (BitDataValue) result;
801:
802: /* The result is null if the receiver (this) is null or if the length is negative.
803: * Oracle docs don't say what happens if the start position or the length is a usernull.
804: * We will return null, which is the only sensible thing to do.
805: * (If the user did not specify a length then length is not a user null.)
806: */
807: if (this .isNull() || start.isNull()
808: || (length != null && length.isNull())) {
809: varbitResult.setToNull();
810: return varbitResult;
811: }
812:
813: startInt = start.getInt();
814:
815: // If length is not specified, make it till end of the string
816: if (length != null) {
817: lengthInt = length.getInt();
818: } else
819: lengthInt = getLength() - startInt + 1;
820:
821: /* DB2 Compatibility: Added these checks to match DB2. We currently enforce these
822: * limits in both modes. We could do these checks in DB2 mode only, if needed, so
823: * leaving earlier code for out of range in for now, though will not be exercised
824: */
825: if ((startInt <= 0 || lengthInt < 0 || startInt > getLength() || lengthInt > getLength()
826: - startInt + 1))
827: throw StandardException
828: .newException(SQLState.LANG_SUBSTR_START_OR_LEN_OUT_OF_RANGE);
829:
830: // Return null if length is non-positive
831: if (lengthInt < 0) {
832: varbitResult.setToNull();
833: return varbitResult;
834: }
835:
836: /* If startInt < 0 then we count from the right of the string */
837: if (startInt < 0) {
838: startInt += getLength();
839: if (startInt < 0) {
840: lengthInt += startInt;
841: startInt = 0;
842: }
843: if (lengthInt + startInt > 0) {
844: lengthInt += startInt;
845: } else {
846: lengthInt = 0;
847: }
848: } else if (startInt > 0) {
849: /* java substr() is 0 based */
850: startInt--;
851: }
852:
853: /* Oracle docs don't say what happens if the window is to the
854: * left of the string. Return "" if the window
855: * is to the left or right or if the length is 0.
856: */
857: if (lengthInt == 0 || lengthInt <= 0 - startInt
858: || startInt > getLength()) {
859: varbitResult.setValue(new byte[0]);
860: return varbitResult;
861: }
862:
863: if (lengthInt >= getLength() - startInt) {
864: byte[] substring = new byte[dataValue.length - startInt];
865: System.arraycopy(dataValue, startInt, substring, 0,
866: substring.length);
867: varbitResult.setValue(substring);
868: } else {
869: byte[] substring = new byte[lengthInt];
870: System.arraycopy(dataValue, startInt, substring, 0,
871: substring.length);
872: varbitResult.setValue(substring);
873: }
874:
875: return varbitResult;
876: }
877:
878: /**
879: Host variables are rejected if their length is
880: bigger than the declared length, regardless of
881: if the trailing bytes are the pad character.
882:
883: @exception StandardException Variable is too big.
884: */
885: public final void checkHostVariable(int declaredLength)
886: throws StandardException {
887: // stream length checking occurs at the JDBC layer
888: int variableLength = -1;
889: if (stream == null) {
890: if (dataValue != null)
891: variableLength = dataValue.length;
892: } else {
893: variableLength = streamValueLength;
894: }
895:
896: if (variableLength != -1 && variableLength > declaredLength)
897: throw StandardException.newException(
898: SQLState.LANG_STRING_TRUNCATION, getTypeName(),
899: "XX-RESOLVE-XX", String.valueOf(declaredLength));
900: }
901:
902: /*
903: * String display of value
904: */
905:
906: public final String toString() {
907: if (dataValue == null) {
908: if (stream == null) {
909: return "NULL";
910: } else {
911: if (SanityManager.DEBUG)
912: SanityManager
913: .THROWASSERT("value is null, stream is not null");
914: return "";
915: }
916: } else {
917: return org.apache.derby.iapi.util.StringUtil.toHexString(
918: dataValue, 0, dataValue.length);
919: }
920: }
921:
922: /*
923: * Hash code
924: */
925: public final int hashCode() {
926: try {
927: if (getValue() == null) {
928: return 0;
929: }
930: } catch (StandardException se) {
931: if (SanityManager.DEBUG)
932: SanityManager.THROWASSERT("Unexpected exception " + se);
933: return 0;
934: }
935:
936: /* Hash code is simply the sum of all of the bytes */
937: byte[] bytes = dataValue;
938: int hashcode = 0;
939:
940: // Build the hash code
941: for (int index = 0; index < bytes.length; index++) {
942: byte bv = bytes[index];
943: if (bv != SQLBinary.PAD)
944: hashcode += bytes[index];
945: }
946:
947: return hashcode;
948: }
949:
950: private static int compare(byte[] left, byte[] right) {
951:
952: int minLen = left.length;
953: byte[] longer = right;
954: if (right.length < minLen) {
955: minLen = right.length;
956: longer = left;
957: }
958:
959: for (int i = 0; i < minLen; i++) {
960:
961: int lb = left[i] & 0xff;
962: int rb = right[i] & 0xff;
963:
964: if (lb == rb)
965: continue;
966:
967: return lb - rb;
968: }
969:
970: // complete match on all the bytes for the smallest value.
971:
972: // if the longer value is all pad characters
973: // then the values are equal.
974: for (int i = minLen; i < longer.length; i++) {
975: byte nb = longer[i];
976: if (nb == SQLBinary.PAD)
977: continue;
978:
979: // longer value is bigger.
980: if (left == longer)
981: return 1;
982: return -1;
983: }
984:
985: return 0;
986:
987: }
988:
989: /** Adding this method to ensure that super class' setInto method doesn't get called
990: * that leads to the violation of JDBC spec( untyped nulls ) when batching is turned on.
991: */
992: public void setInto(PreparedStatement ps, int position)
993: throws SQLException, StandardException {
994:
995: ps.setBytes(position, getBytes());
996: }
997: }
|