Source code: org/apache/derby/iapi/types/SQLBinary.java
1 /*
2
3 Derby - Class org.apache.derby.iapi.types.SQLBinary
4
5 Copyright 2004 The Apache Software Foundation or its licensors, as applicable.
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18
19 */
20
21 package org.apache.derby.iapi.types;
22
23 import org.apache.derby.iapi.reference.SQLState;
24
25 import org.apache.derby.iapi.services.io.ArrayInputStream;
26 import org.apache.derby.iapi.services.io.FormatableBitSet;
27 import org.apache.derby.iapi.services.io.NewByteArrayInputStream;
28
29 import org.apache.derby.iapi.types.DataTypeDescriptor;
30 import org.apache.derby.iapi.types.DataValueDescriptor;
31 import org.apache.derby.iapi.types.TypeId;
32 import org.apache.derby.iapi.types.BitDataValue;
33 import org.apache.derby.iapi.types.DataValueDescriptor;
34 import org.apache.derby.iapi.types.ConcatableDataValue;
35 import org.apache.derby.iapi.types.VariableSizeDataValue;
36 import org.apache.derby.iapi.error.StandardException;
37
38 import org.apache.derby.iapi.services.io.FormatIdUtil;
39 import org.apache.derby.iapi.services.io.StoredFormatIds;
40 import org.apache.derby.iapi.services.io.StreamStorable;
41 import org.apache.derby.iapi.services.io.FormatIdInputStream;
42
43 import org.apache.derby.iapi.services.sanity.SanityManager;
44
45 import org.apache.derby.iapi.types.BooleanDataValue;
46 import org.apache.derby.iapi.types.StringDataValue;
47 import org.apache.derby.iapi.types.NumberDataValue;
48
49 import org.apache.derby.iapi.services.cache.ClassSize;
50 import org.apache.derby.iapi.util.StringUtil;
51
52 import org.apache.derby.iapi.types.SQLInteger;
53
54 import java.io.ObjectOutput;
55 import java.io.ObjectInput;
56 import java.io.IOException;
57 import java.io.InputStream;
58
59 import java.sql.ResultSet;
60 import java.sql.SQLException;
61 import java.sql.PreparedStatement;
62
63 /**
64 * SQLBinary satisfies the DataValueDescriptor
65 * interfaces (i.e., DataType). It implements a String holder,
66 * e.g. for storing a column value; it can be specified
67 * when constructed to not allow nulls. Nullability cannot be changed
68 * after construction.
69 * <p>
70 * Because DataType is a subclass of DataType,
71 * SQLBit can play a role in either a DataType/Value
72 * or a DataType/KeyRow, interchangeably.
73
74 <P>
75 Format : <encoded length><raw data>
76 <BR>
77 Length is encoded to support 5.x databases where the length was stored as the number of bits.
78 The first bit of the first byte indicates if the format is an old (5.x) style or a new 8.1 style.
79 8.1 then uses the next two bits to indicate how the length is encoded.
80 <BR>
81 <encoded length> is one of N styles.
82 <UL>
83 <LI> (5.x format) 4 byte Java format integer value 0 - either <raw data> is 0 bytes/bits or an unknown number of bytes.
84 <LI> (5.x format) 4 byte Java format integer value >0 (positive) - number of bits in <raw data>, number of bytes in <raw data>
85 is the minimum number of bytes required to store the number of bits.
86 <LI> (8.1 format) 1 byte encoded length (0 <= L <= 31) - number of bytes of <raw data> - encoded = 0x80 & L
87 <LI> (8.1 format) 3 byte encoded length (32 <= L < 64k) - number of bytes of <raw data> - encoded = 0xA0 <L as Java format unsigned short>
88 <LI> (8.1 format) 5 byte encoded length (64k <= L < 2G) - number of bytes of <raw data> - encoded = 0xC0 <L as Java format integer>
89 <LI> (future) to be determined L >= 2G - encoded 0xE0 <encoding of L to be determined>
90 (0xE0 is an esacape to allow any number of arbitary encodings in the future).
91 </UL>
92 */
93 public abstract class SQLBinary
94 extends DataType implements BitDataValue
95 {
96
97 static final byte PAD = (byte) 0x20;
98
99 private static final int BASE_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( SQLBinary.class);
100
101 public int estimateMemoryUsage()
102 {
103 int sz = BASE_MEMORY_USAGE;
104 if( null != dataValue)
105 sz += dataValue.length;
106 return sz;
107 } // end of estimateMemoryUsage
108
109
110
111 /*
112 * object state
113 */
114 byte[] dataValue;
115
116 /*
117 * stream state
118 */
119 InputStream stream;
120
121 /**
122 Length of the stream in units relevant to the type,
123 in this case bytes.
124 */
125 int streamLength;
126
127 /**
128 no-arg constructor, required by Formattable.
129 */
130 SQLBinary()
131 {
132 }
133
134 SQLBinary(byte[] val)
135 {
136 dataValue = val;
137 }
138
139
140 public final void setValue(byte[] theValue)
141 {
142 dataValue = theValue;
143 stream = null;
144 streamLength = -1;
145 }
146
147 /**
148 * Used by JDBC -- string should not contain
149 * SQL92 formatting.
150 *
151 * @exception StandardException Thrown on error
152 */
153 public final String getString() throws StandardException
154 {
155 if (getValue() == null)
156 return null;
157 else if (dataValue.length * 2 < 0) //if converted to hex, length exceeds max int
158 {
159 throw StandardException.newException(SQLState.LANG_STRING_TRUNCATION, getTypeName(),
160 "",
161 String.valueOf(Integer.MAX_VALUE));
162 }
163 else
164 {
165 return org.apache.derby.iapi.util.StringUtil.toHexString(dataValue, 0, dataValue.length);
166 }
167 }
168
169
170 /**
171 * @exception StandardException Thrown on error
172 */
173 public final InputStream getStream()
174 {
175 return (stream);
176 }
177
178 /**
179 *
180 * @exception StandardException Thrown on error
181 */
182 public final byte[] getBytes() throws StandardException
183 {
184 return getValue();
185 }
186
187 byte[] getValue() throws StandardException
188 {
189 try
190 {
191 if ((dataValue == null) && (stream != null)) {
192
193 if (stream instanceof FormatIdInputStream) {
194 readExternal((FormatIdInputStream) stream);
195 } else if ( stream instanceof NewByteArrayInputStream )
196 {
197 // this piece of code handles the case that a stream has been
198 // opened on the bit value. the stream will have already called
199 // readExternal() on the underlying FormatableBitSet. we just need to
200 // retrieve the byte array from that stream.
201 NewByteArrayInputStream nbais = (NewByteArrayInputStream) stream;
202 dataValue = nbais.getData();
203 }
204 else {
205 readExternal(new FormatIdInputStream(stream));
206 }
207 stream = null;
208 streamLength = -1;
209
210 }
211 }
212 catch (IOException ioe)
213 {
214 throw StandardException.newException(SQLState.LANG_STREAMING_COLUMN_I_O_EXCEPTION, ioe, getTypeName());
215 }
216 return dataValue;
217 }
218
219 /**
220 * length in bytes
221 *
222 * @exception StandardException Thrown on error
223 */
224 public final int getLength() throws StandardException
225 {
226 if (stream != null) {
227
228 if (streamLength != -1)
229 return streamLength;
230 }
231
232 return getBytes().length;
233
234 }
235
236 /*
237 * Storable interface, implies Externalizable, TypedFormat
238 */
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 {
247 return (dataValue == null) && (stream == null);
248 }
249
250 /**
251 Write the value out from the byte array (not called if null)
252 using the 8.1 encoding.
253
254 * @exception IOException io exception
255 */
256 public final void writeExternal(ObjectOutput out) throws IOException
257 {
258
259 int len = dataValue.length;
260 if (len <= 31)
261 {
262 out.write((byte) (0x80 | (len & 0xff)));
263 }
264 else if (len <= 0xFFFF)
265 {
266 out.write((byte) 0xA0);
267 out.writeShort((short) len);
268 }
269 else
270 {
271 out.write((byte) 0xC0);
272 out.writeInt(len);
273
274 }
275 out.write(dataValue, 0, dataValue.length);
276 }
277
278 /**
279 * delegated to bit
280 *
281 * @exception IOException io exception
282 * @exception ClassNotFoundException class not found
283 */
284 public final void readExternal(ObjectInput in) throws IOException
285 {
286 // need to clear stream first, in case this object is reused, and
287 // stream is set by previous use. Track 3794.
288 stream = null;
289 streamLength = -1;
290
291
292 int len = SQLBinary.readBinaryLength(in);
293
294 if (len != 0)
295 {
296 dataValue = new byte[len];
297 in.readFully(dataValue);
298 }
299 else
300 {
301 readFromStream((InputStream) in);
302 }
303 }
304 public final void readExternalFromArray(ArrayInputStream in) throws IOException
305 {
306 // need to clear stream first, in case this object is reused, and
307 // stream is set by previous use. Track 3794.
308 stream = null;
309 streamLength = -1;
310
311 int len = SQLBinary.readBinaryLength(in);
312
313 if (len != 0)
314 {
315 dataValue = new byte[len];
316 in.readFully(dataValue);
317 }
318 else
319 {
320 readFromStream(in);
321 }
322 }
323
324 private static int readBinaryLength(ObjectInput in) throws IOException {
325 int len = 0;
326 int bl = in.read();
327 if (len < 0)
328 throw new java.io.EOFException();
329
330 if ((bl & 0x80) != 0)
331 {
332 if (bl == 0xC0)
333 {
334 len = in.readInt();
335 }
336 else if (bl == 0xA0)
337 {
338 len = in.readUnsignedShort();
339 }
340 else
341 {
342 len = bl & 0x1F;
343 }
344 }
345 else
346 {
347 // old length in bits
348 int v2 = in.read();
349 int v3 = in.read();
350 int v4 = in.read();
351 if (v2 < 0 || v3 < 0 || v4 < 0)
352 throw new java.io.EOFException();
353 int lenInBits = (((bl & 0xff) << 24) | ((v2 & 0xff) << 16) | ((v3 & 0xff) << 8) | (v4 & 0xff));
354
355 len = lenInBits / 8;
356 if ((lenInBits % 8) != 0)
357 len++;
358 }
359 return len;
360 }
361
362 private void readFromStream(InputStream in) throws IOException {
363
364 dataValue = null; // allow gc of the old value before the new.
365 byte[] tmpData = new byte[32 * 1024];
366
367 int off = 0;
368 for (;;) {
369
370 int len = in.read(tmpData, off, tmpData.length - off);
371 if (len == -1)
372 break;
373 off += len;
374
375 int available = in.available();
376 int extraSpace = available - (tmpData.length - off);
377 if (extraSpace > 0)
378 {
379 // need to grow the array
380 int size = tmpData.length * 2;
381 if (extraSpace > tmpData.length)
382 size += extraSpace;
383
384 byte[] grow = new byte[size];
385 System.arraycopy(tmpData, 0, grow, 0, off);
386 tmpData = grow;
387 }
388 }
389
390 dataValue = new byte[off];
391 System.arraycopy(tmpData, 0, dataValue, 0, off);
392 }
393
394 /**
395 * @see org.apache.derby.iapi.services.io.Storable#restoreToNull
396 */
397 public final void restoreToNull()
398 {
399 dataValue = null;
400 stream = null;
401 streamLength = -1;
402 }
403
404 /**
405 @exception StandardException thrown on error
406 */
407 public final boolean compare(int op,
408 DataValueDescriptor other,
409 boolean orderedNulls,
410 boolean unknownRV)
411 throws StandardException
412 {
413 if (!orderedNulls) // nulls are unordered
414 {
415 if (SanityManager.DEBUG)
416 {
417 int otherTypeFormatId = other.getTypeFormatId();
418 if (!((StoredFormatIds.SQL_BIT_ID == otherTypeFormatId)
419 || (StoredFormatIds.SQL_VARBIT_ID == otherTypeFormatId)
420 || (StoredFormatIds.SQL_LONGVARBIT_ID == otherTypeFormatId)
421
422 || (StoredFormatIds.SQL_CHAR_ID == otherTypeFormatId)
423 || (StoredFormatIds.SQL_VARCHAR_ID == otherTypeFormatId)
424 || (StoredFormatIds.SQL_LONGVARCHAR_ID == otherTypeFormatId)
425
426 || ((StoredFormatIds.SQL_BLOB_ID == otherTypeFormatId)
427 && (StoredFormatIds.SQL_BLOB_ID == getTypeFormatId()))
428 ))
429 SanityManager.THROWASSERT(
430 "Some fool passed in a "+ other.getClass().getName() + ", "
431 + otherTypeFormatId + " to SQLBinary.compare()");
432 }
433 String otherString = other.getString();
434 if (this.getString() == null || otherString == null)
435 return unknownRV;
436 }
437 /* Do the comparison */
438 return super.compare(op, other, orderedNulls, unknownRV);
439 }
440
441 /**
442 @exception StandardException thrown on error
443 */
444 public final int compare(DataValueDescriptor other) throws StandardException
445 {
446
447 /* Use compare method from dominant type, negating result
448 * to reflect flipping of sides.
449 */
450 if (typePrecedence() < other.typePrecedence())
451 {
452 return - (other.compare(this));
453 }
454
455 /*
456 ** By convention, nulls sort High, and null == null
457 */
458 if (this.isNull() || other.isNull())
459 {
460 if (!isNull())
461 return -1;
462 if (!other.isNull())
463 return 1;
464 return 0; // both null
465 }
466
467 return SQLBinary.compare(getBytes(), other.getBytes());
468 }
469
470 /*
471 * CloneableObject interface
472 */
473
474 /** From CloneableObject
475 * Shallow clone a StreamStorable without objectifying. This is used to avoid
476 * unnecessary objectifying of a stream object. The only difference of this method
477 * from getClone is this method does not objectify a stream. beetle 4896
478 */
479 public final Object cloneObject()
480 {
481 if (stream == null)
482 return getClone();
483 SQLBinary self = (SQLBinary) getNewNull();
484 self.setStream(stream);
485 return self;
486 }
487
488 /*
489 * DataValueDescriptor interface
490 */
491
492 /** @see DataValueDescriptor#getClone */
493 public final DataValueDescriptor getClone()
494 {
495 try
496 {
497 DataValueDescriptor cloneDVD = getNewNull();
498 cloneDVD.setValue(getValue());
499 return cloneDVD;
500 }
501 catch (StandardException se)
502 {
503 if (SanityManager.DEBUG)
504 SanityManager.THROWASSERT("Unexpected exception " + se);
505 return null;
506 }
507 }
508
509 /*
510 * DataValueDescriptor interface
511 */
512
513 /*
514 * StreamStorable interface :
515 */
516 public final InputStream returnStream()
517 {
518 return stream;
519 }
520
521 public final void setStream(InputStream newStream)
522 {
523 this.dataValue = null;
524 this.stream = newStream;
525 streamLength = -1;
526 }
527
528 public final void loadStream() throws StandardException
529 {
530 getValue();
531 }
532
533 /*
534 * class interface
535 */
536
537 boolean objectNull(Object o)
538 {
539 if (o == null)
540 {
541 setToNull();
542 return true;
543 }
544 return false;
545 }
546
547 /**
548 * @see SQLBit#setValue
549 *
550 */
551 public final void setValue(InputStream theStream, int streamLength)
552 {
553 dataValue = null;
554 stream = theStream;
555 this.streamLength = streamLength;
556 }
557
558 protected final void setFrom(DataValueDescriptor theValue) throws StandardException {
559
560 if (theValue instanceof SQLBinary)
561 {
562 SQLBinary theValueBinary = (SQLBinary) theValue;
563 dataValue = theValueBinary.dataValue;
564 stream = theValueBinary.stream;
565 streamLength = theValueBinary.streamLength;
566 }
567 else
568 {
569 setValue(theValue.getBytes());
570 }
571 }
572
573 /*
574 ** SQL Operators
575 */
576
577 /**
578 * The = operator as called from the language module, as opposed to
579 * the storage module.
580 *
581 * @param left The value on the left side of the =
582 * @param right The value on the right side of the =
583 * is not.
584 * @return A SQL boolean value telling whether the two parameters are equal
585 *
586 * @exception StandardException Thrown on error
587 */
588
589 public final BooleanDataValue equals(DataValueDescriptor left,
590 DataValueDescriptor right)
591 throws StandardException
592 {
593 boolean isEqual;
594
595 if (left.isNull() || right.isNull())
596 {
597 isEqual = false;
598 }
599 else
600 {
601 isEqual = SQLBinary.compare(left.getBytes(), right.getBytes()) == 0;
602 }
603
604 return SQLBoolean.truthValue(left,
605 right,
606 isEqual);
607 }
608
609 /**
610 * The <> operator as called from the language module, as opposed to
611 * the storage module.
612 *
613 * @param left The value on the left side of the <>
614 * @param right The value on the right side of the <>
615 *
616 * @return A SQL boolean value telling whether the two parameters
617 * are not equal
618 *
619 * @exception StandardException Thrown on error
620 */
621
622 public final BooleanDataValue notEquals(DataValueDescriptor left,
623 DataValueDescriptor right)
624 throws StandardException
625 {
626 boolean isNotEqual;
627
628 if (left.isNull() || right.isNull())
629 {
630 isNotEqual = false;
631 }
632 else
633 {
634 isNotEqual = SQLBinary.compare(left.getBytes(), right.getBytes()) != 0;
635 }
636
637 return SQLBoolean.truthValue(left,
638 right,
639 isNotEqual);
640 }
641
642 /**
643 * The < operator as called from the language module, as opposed to
644 * the storage module.
645 *
646 * @param left The value on the left side of the <
647 * @param right The value on the right side of the <
648 * @param result The result of a previous call to this method, null
649 * if not called yet. NOTE: This is unused in this
650 * method, because comparison operators always return
651 * pre-allocated values.
652 *
653 * @return A SQL boolean value telling whether the first operand is
654 * less than the second operand
655 *
656 * @exception StandardException Thrown on error
657 */
658
659 public final BooleanDataValue lessThan(DataValueDescriptor left,
660 DataValueDescriptor right)
661 throws StandardException
662 {
663 boolean isLessThan;
664
665 if (left.isNull() || right.isNull())
666 {
667 isLessThan = false;
668 }
669 else
670 {
671 isLessThan = SQLBinary.compare(left.getBytes(), right.getBytes()) < 0;
672 }
673
674 return SQLBoolean.truthValue(left,
675 right,
676 isLessThan);
677 }
678
679 /**
680 * The > operator as called from the language module, as opposed to
681 * the storage module.
682 *
683 * @param left The value on the left side of the >
684 * @param right The value on the right side of the >
685 *
686 * @return A SQL boolean value telling whether the first operand is
687 * greater than the second operand
688 *
689 * @exception StandardException Thrown on error
690 */
691
692 public final BooleanDataValue greaterThan(DataValueDescriptor left,
693 DataValueDescriptor right)
694 throws StandardException
695 {
696 boolean isGreaterThan = false;
697
698 if (left.isNull() || right.isNull())
699 {
700 isGreaterThan = false;
701 }
702 else
703 {
704 isGreaterThan = SQLBinary.compare(left.getBytes(), right.getBytes()) > 0;
705 }
706
707 return SQLBoolean.truthValue(left,
708 right,
709 isGreaterThan);
710 }
711
712 /**
713 * The <= operator as called from the language module, as opposed to
714 * the storage module.
715 *
716 * @param left The value on the left side of the <=
717 * @param right The value on the right side of the <=
718 *
719 * @return A SQL boolean value telling whether the first operand is
720 * less than or equal to the second operand
721 *
722 * @exception StandardException Thrown on error
723 */
724
725 public final BooleanDataValue lessOrEquals(DataValueDescriptor left,
726 DataValueDescriptor right)
727 throws StandardException
728 {
729 boolean isLessEquals = false;
730
731 if (left.isNull() || right.isNull())
732 {
733 isLessEquals = false;
734 }
735 else
736 {
737 isLessEquals = SQLBinary.compare(left.getBytes(), right.getBytes()) <= 0;
738 }
739
740 return SQLBoolean.truthValue(left,
741 right,
742 isLessEquals);
743 }
744
745 /**
746 * The >= operator as called from the language module, as opposed to
747 * the storage module.
748 *
749 * @param left The value on the left side of the >=
750 * @param right The value on the right side of the >=
751 *
752 * @return A SQL boolean value telling whether the first operand is
753 * greater than or equal to the second operand
754 *
755 * @exception StandardException Thrown on error
756 */
757
758 public final BooleanDataValue greaterOrEquals(DataValueDescriptor left,
759 DataValueDescriptor right)
760 throws StandardException
761 {
762 boolean isGreaterEquals = false;
763
764 if (left.isNull() || right.isNull())
765 {
766 isGreaterEquals = false;
767 }
768 else
769 {
770 isGreaterEquals = SQLBinary.compare(left.getBytes(), right.getBytes()) >= 0;
771 }
772
773 return SQLBoolean.truthValue(left,
774 right,
775 isGreaterEquals);
776 }
777
778
779 /**
780 *
781 * This method implements the char_length function for bit.
782 *
783 * @param result The result of a previous call to this method, null
784 * if not called yet
785 *
786 * @return A SQLInteger containing the length of the char value
787 *
788 * @exception StandardException Thrown on error
789 *
790 * @see ConcatableDataValue#charLength
791 */
792
793 public final NumberDataValue charLength(NumberDataValue result)
794 throws StandardException
795 {
796 if (result == null)
797 {
798 result = new SQLInteger();
799 }
800
801 if (this.isNull())
802 {
803 result.setToNull();
804 return result;
805 }
806
807
808 result.setValue(getValue().length);
809 return result;
810 }
811
812 /**
813 * @see BitDataValue#concatenate
814 *
815 * @exception StandardException Thrown on error
816 */
817 public final BitDataValue concatenate(
818 BitDataValue left,
819 BitDataValue right,
820 BitDataValue result)
821 throws StandardException
822 {
823 if (left.isNull() || right.isNull())
824 {
825 result.setToNull();
826 return result;
827 }
828
829 byte[] leftData = left.getBytes();
830 byte[] rightData = right.getBytes();
831
832 byte[] concatData = new byte[leftData.length + rightData.length];
833
834 System.arraycopy(leftData, 0, concatData, 0, leftData.length);
835 System.arraycopy(rightData, 0, concatData, leftData.length, rightData.length);
836
837
838 result.setValue(concatData);
839 return result;
840 }
841
842
843 /**
844 * The SQL substr() function.
845 *
846 * @param start Start of substr
847 * @param length Length of substr
848 * @param result The result of a previous call to this method,
849 * null if not called yet.
850 * @param maxLen Maximum length of the result
851 *
852 * @return A ConcatableDataValue containing the result of the substr()
853 *
854 * @exception StandardException Thrown on error
855 */
856 public final ConcatableDataValue substring(
857 NumberDataValue start,
858 NumberDataValue length,
859 ConcatableDataValue result,
860 int maxLen)
861 throws StandardException
862 {
863 int startInt;
864 int lengthInt;
865 BitDataValue varbitResult;
866
867 if (result == null)
868 {
869 result = new SQLVarbit();
870 }
871
872 varbitResult = (BitDataValue) result;
873
874 /* The result is null if the receiver (this) is null or if the length is negative.
875 * Oracle docs don't say what happens if the start position or the length is a usernull.
876 * We will return null, which is the only sensible thing to do.
877 * (If the user did not specify a length then length is not a user null.)
878 */
879 if (this.isNull() || start.isNull() || (length != null && length.isNull()))
880 {
881 varbitResult.setToNull();
882 return varbitResult;
883 }
884
885 startInt = start.getInt();
886
887 // If length is not specified, make it till end of the string
888 if (length != null)
889 {
890 lengthInt = length.getInt();
891 }
892 else lengthInt = getLength() - startInt + 1;
893
894 /* DB2 Compatibility: Added these checks to match DB2. We currently enforce these
895 * limits in both modes. We could do these checks in DB2 mode only, if needed, so
896 * leaving earlier code for out of range in for now, though will not be exercised
897 */
898 if ((startInt <= 0 || lengthInt < 0 || startInt > getLength() ||
899 lengthInt > getLength() - startInt + 1))
900 throw StandardException.newException(SQLState.LANG_SUBSTR_START_OR_LEN_OUT_OF_RANGE);
901
902 // Return null if length is non-positive
903 if (lengthInt < 0)
904 {
905 varbitResult.setToNull();
906 return varbitResult;
907 }
908
909 /* If startInt < 0 then we count from the right of the string */
910 if (startInt < 0)
911 {
912 startInt += getLength();
913 if (startInt < 0)
914 {
915 lengthInt += startInt;
916 startInt = 0;
917 }
918 if (lengthInt + startInt > 0)
919 {
920 lengthInt += startInt;
921 }
922 else
923 {
924 lengthInt = 0;
925 }
926 }
927 else if (startInt > 0)
928 {
929 /* java substr() is 0 based */
930 startInt--;
931 }
932
933 /* Oracle docs don't say what happens if the window is to the
934 * left of the string. Return "" if the window
935 * is to the left or right or if the length is 0.
936 */
937 if (lengthInt == 0 ||
938 lengthInt <= 0 - startInt ||
939 startInt > getLength())
940 {
941 varbitResult.setValue(new byte[0]);
942 return varbitResult;
943 }
944
945 if (lengthInt >= getLength() - startInt)
946 {
947 byte[] substring = new byte[dataValue.length - startInt];
948 System.arraycopy(dataValue, startInt, substring, 0, substring.length);
949 varbitResult.setValue(substring);
950 }
951 else
952 {
953 byte[] substring = new byte[lengthInt];
954 System.arraycopy(dataValue, startInt, substring, 0, substring.length);
955 varbitResult.setValue(substring);
956 }
957
958 return varbitResult;
959 }
960
961 /**
962 Host variables are rejected if their length is
963 bigger than the declared length, regardless of
964 if the trailing bytes are the pad character.
965
966 @exception StandardException Variable is too big.
967 */
968 public final void checkHostVariable(int declaredLength) throws StandardException
969 {
970 // stream length checking occurs at the JDBC layer
971 int variableLength = -1;
972 if (stream == null)
973 {
974 if (dataValue != null)
975 variableLength = dataValue.length;
976 }
977 else
978 {
979 variableLength = streamLength;
980 }
981
982 if (variableLength != -1 && variableLength > declaredLength)
983 throw StandardException.newException(SQLState.LANG_STRING_TRUNCATION, getTypeName(),
984 "XX-RESOLVE-XX",
985 String.valueOf(declaredLength));
986 }
987
988 /*
989 * String display of value
990 */
991
992 public final String toString()
993 {
994 if (dataValue == null)
995 {
996 if (stream == null)
997 {
998 return "NULL";
999 }
1000 else
1001 {
1002 if (SanityManager.DEBUG)
1003 SanityManager.THROWASSERT(
1004 "value is null, stream is not null");
1005 return "";
1006 }
1007 }
1008 else
1009 {
1010 return org.apache.derby.iapi.util.StringUtil.toHexString(dataValue, 0, dataValue.length);
1011 }
1012 }
1013
1014 /*
1015 * Hash code
1016 */
1017 public final int hashCode()
1018 {
1019 try {
1020 if (getValue() == null)
1021 {
1022 return 0;
1023 }
1024 }
1025 catch (StandardException se)
1026 {
1027 if (SanityManager.DEBUG)
1028 SanityManager.THROWASSERT("Unexpected exception " + se);
1029 return 0;
1030 }
1031
1032 /* Hash code is simply the sum of all of the bytes */
1033 byte[] bytes = dataValue;
1034 int hashcode = 0;
1035
1036 // Build the hash code
1037 for (int index = 0 ; index < bytes.length; index++)
1038 {
1039 byte bv = bytes[index];
1040 if (bv != SQLBinary.PAD)
1041 hashcode += bytes[index];
1042 }
1043
1044 return hashcode;
1045 }
1046 private static int compare(byte[] left, byte[] right) {
1047
1048 int minLen = left.length;
1049 byte[] longer = right;
1050 if (right.length < minLen) {
1051 minLen = right.length;
1052 longer = left;
1053 }
1054
1055 for (int i = 0; i < minLen; i++) {
1056
1057 int lb = left[i] & 0xff;
1058 int rb = right[i] & 0xff;
1059
1060 if (lb == rb)
1061 continue;
1062
1063 return lb - rb;
1064 }
1065
1066 // complete match on all the bytes for the smallest value.
1067
1068 // if the longer value is all pad characters
1069 // then the values are equal.
1070 for (int i = minLen; i < longer.length; i++) {
1071 byte nb = longer[i];
1072 if (nb == SQLBinary.PAD)
1073 continue;
1074
1075 // longer value is bigger.
1076 if (left == longer)
1077 return 1;
1078 return -1;
1079 }
1080
1081 return 0;
1082
1083 }
1084
1085 /** Adding this method to ensure that super class' setInto method doesn't get called
1086 * that leads to the violation of JDBC spec( untyped nulls ) when batching is turned on.
1087 */
1088 public void setInto(PreparedStatement ps, int position) throws SQLException, StandardException {
1089
1090 ps.setBytes(position, getBytes());
1091 }
1092}