001: /*
002:
003: Derby - Class org.apache.derby.impl.jdbc.EmbedBlob
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.impl.jdbc;
023:
024: import org.apache.derby.iapi.reference.SQLState;
025: import org.apache.derby.iapi.error.StandardException;
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027: import org.apache.derby.iapi.types.DataValueDescriptor;
028: import org.apache.derby.iapi.types.Resetable;
029: import org.apache.derby.impl.jdbc.ConnectionChild;
030: import org.apache.derby.impl.jdbc.EmbedConnection;
031: import org.apache.derby.impl.jdbc.Util;
032: import org.apache.derby.iapi.services.io.NewByteArrayInputStream;
033: import org.apache.derby.iapi.services.io.InputStreamUtil;
034: import org.apache.derby.iapi.services.io.ArrayInputStream;
035:
036: import java.sql.SQLException;
037: import java.sql.Blob;
038: import java.io.InputStream;
039: import java.io.EOFException;
040: import java.io.IOException;
041:
042: /**
043: Implements java.sql.Blob (see the JDBC 2.0 spec).
044: A blob sits on top of a BINARY, VARBINARY or LONG VARBINARY column.
045: If its data is small (less than 1 page) it is a byte array taken from
046: the SQLBit class. If it is large (more than 1 page) it is a long column
047: in the database. The long column is accessed as a stream, and is implemented
048: in store as an OverflowInputStream. The Resetable interface allows sending
049: messages to that stream to initialize itself (reopen its container and
050: lock the corresponding row) and to reset itself to the beginning.
051:
052: NOTE: In the case that the data is large, it is represented as a stream.
053: This stream is returned to the user in the getBinaryStream() method.
054: This means that we have limited control over the state of the stream,
055: since the user can read bytes from it at any time. Thus all methods
056: here reset the stream to the beginning before doing any work.
057: CAVEAT: The methods may not behave correctly if a user sets up
058: multiple threads and sucks data from the stream (returned from
059: getBinaryStream()) at the same time as calling the Blob methods.
060:
061: <P><B>Supports</B>
062: <UL>
063: <LI> JSR169 - no subsetting for java.sql.Blob
064: <LI> JDBC 2.0
065: <LI> JDBC 3.0 - no new dependencies on new JDBC 3.0 or JDK 1.4 classes,
066: new update methods can safely be added into implementation.
067: </UL>
068:
069: */
070:
071: final class EmbedBlob extends ConnectionChild implements Blob {
072: // blob is either bytes or stream
073: private boolean isBytes;
074: private InputStream myStream;
075:
076: /*
077: * Length of the BLOB if known. Set to -1 if
078: * the current length of the BLOB is not known.
079: */
080: private long myLength = -1;
081:
082: private byte[] myBytes;
083: // note: cannot control position of the stream since user can do a getBinaryStream
084: private long pos;
085: // this stream sits on top of myStream
086: private BinaryToRawStream biStream;
087:
088: // buffer for reading in blobs from a stream (long column)
089: // and trashing them (to set the position of the stream etc.)
090: private static int BLOB_BUF_SIZE = 4096;
091: private byte buf[];
092:
093: //This boolean variable indicates whether the Blob object has
094: //been invalidated by calling free() on it
095: private boolean isValid = true;
096:
097: /**
098: * This constructor is used to create a empty Blob object. It is used by the
099: * Connection interface method createBlob().
100: *
101: * @param blobBytes A byte array containing the data to be stores in the
102: * Blob.
103: *
104: * @param con The EmbedConnection object associated with this Blob object.
105: *
106: */
107:
108: EmbedBlob(byte[] blobBytes, EmbedConnection con) {
109: super (con);
110: myBytes = blobBytes;
111: isBytes = true;
112: myLength = myBytes.length;
113: }
114:
115: /*
116: This constructor should only be called by EmbedResultSet.getBlob
117: */
118: protected EmbedBlob(DataValueDescriptor dvd, EmbedConnection con)
119: throws StandardException {
120: super (con);
121: // if the underlying column is null, ResultSet.getBlob will return null,
122: // never should get this far
123: if (SanityManager.DEBUG)
124: SanityManager.ASSERT(!dvd.isNull(),
125: "blob is created on top of a null column");
126:
127: myStream = dvd.getStream();
128: if (myStream == null) {
129: isBytes = true;
130: // copy bytes into memory so that blob can live after result set
131: // is closed
132: byte[] dvdBytes = dvd.getBytes();
133:
134: if (SanityManager.DEBUG)
135: SanityManager.ASSERT(dvdBytes != null,
136: "blob has a null value underneath");
137:
138: myLength = dvdBytes.length;
139: myBytes = new byte[dvdBytes.length];
140: System.arraycopy(dvdBytes, 0, myBytes, 0, dvdBytes.length);
141: } else {
142: isBytes = false;
143:
144: /*
145: We are expecting this stream to be a FormatIdInputStream with an
146: OverflowInputStream inside. FormatIdInputStream implements
147: Resetable. This should be the case when retrieving
148: data from a long column. However, SQLBit, which is the class
149: implementing the getStream() method for dvd.getStream(), does not
150: guarantee this for us
151: */
152: if (SanityManager.DEBUG)
153: SanityManager.ASSERT(myStream instanceof Resetable);
154:
155: try {
156: ((Resetable) myStream).initStream();
157: } catch (StandardException se) {
158: if (se.getMessageId().equals(
159: SQLState.DATA_CONTAINER_CLOSED)) {
160: throw StandardException
161: .newException(SQLState.BLOB_ACCESSED_AFTER_COMMIT);
162: }
163: }
164: // set up the buffer for trashing the bytes to set the position of
165: // the
166: // stream, only need a buffer when we have a long column
167: buf = new byte[BLOB_BUF_SIZE];
168: }
169: pos = 0;
170: }
171:
172: /*
173: Sets the position of the stream to position newPos, where position 0 is
174: the beginning of the stream.
175:
176: @param newPos the position to set to
177: @exception StandardException (BLOB_SETPOSITION_FAILED) throws this if
178: the stream runs out before we get to newPos
179: */
180: private void setPosition(long newPos) throws StandardException,
181: IOException {
182: if (SanityManager.DEBUG)
183: SanityManager.ASSERT(newPos >= 0);
184: if (isBytes)
185: pos = newPos;
186: else {
187: // Always resets the stream to the beginning first, because user can
188: // influence the state of the stream without letting us know.
189: ((Resetable) myStream).resetStream();
190: // PT could try to save creating a new object each time
191: biStream = new BinaryToRawStream(myStream, this );
192: pos = 0;
193: while (pos < newPos) {
194: int size = biStream.read(buf, 0, (int) Math.min(
195: (newPos - pos), (long) BLOB_BUF_SIZE));
196: if (size <= 0) // ran out of stream
197: throw StandardException
198: .newException(SQLState.BLOB_LENGTH_TOO_LONG);
199: pos += size;
200: }
201: }
202: }
203:
204: /*
205: Reads one byte, either from the byte array or else from the stream.
206: */
207: private int read() throws IOException {
208: int c;
209: if (isBytes) {
210: if (pos >= myBytes.length)
211: return -1;
212: else
213: c = myBytes[(int) pos];
214: } else
215: c = biStream.read();
216: pos++;
217: return c;
218: }
219:
220: /**
221: * Returns the number of bytes in the <code>BLOB</code> value
222: * designated by this <code>Blob</code> object.
223: * @return length of the <code>BLOB</code> in bytes
224: * @exception SQLException if there is an error accessing the
225: * length of the <code>BLOB</code>
226: */
227: // PT stream part may get pushed to store
228: public long length() throws SQLException {
229: //call checkValidity to exit by throwing a SQLException if
230: //the Blob object has been freed by calling free() on it
231: checkValidity();
232:
233: if (myLength != -1)
234: return myLength;
235:
236: boolean pushStack = false;
237: try {
238: // we have a stream
239: synchronized (getConnectionSynchronization()) {
240: pushStack = !getEmbedConnection().isClosed();
241: if (pushStack)
242: setupContextStack();
243:
244: setPosition(0);
245: // If possible get the length from the encoded
246: // length at the front of the raw stream.
247: if ((myLength = biStream.getLength()) != -1) {
248: biStream.close();
249: return myLength;
250: }
251:
252: // Otherwise have to read the entire stream!
253: for (;;) {
254: int size = biStream.read(buf);
255: if (size == -1)
256: break;
257: pos += size;
258: }
259: // Save for future uses.
260: myLength = pos;
261: biStream.close();
262: return pos;
263: }
264: } catch (Throwable t) {
265: throw handleMyExceptions(t);
266: } finally {
267: if (pushStack)
268: restoreContextStack();
269: }
270: }
271:
272: /**
273: * Returns as an array of bytes part or all of the <code>BLOB</code>
274: * value that this <code>Blob</code> object designates. The byte
275: * array contains up to <code>length</code> consecutive bytes
276: * starting at position <code>startPos</code>.
277: * The starting position must be between 1 and the length
278: * of the BLOB plus 1. This allows for zero-length BLOB values, from
279: * which only zero-length byte arrays can be returned.
280: * If a larger length is requested than there are bytes available,
281: * characters from the start position to the end of the BLOB are returned.
282: * @param startPos the ordinal position of the first byte in the
283: * <code>BLOB</code> value to be extracted; the first byte is at
284: * position 1
285: * @param length is the number of consecutive bytes to be copied
286: * @return a byte array containing up to <code>length</code>
287: * consecutive bytes from the <code>BLOB</code> value designated
288: * by this <code>Blob</code> object, starting with the
289: * byte at position <code>startPos</code>.
290: * @exception SQLException if there is an error accessing the
291: * <code>BLOB</code>
292: * NOTE: If the starting position is the length of the BLOB plus 1,
293: * zero bytess are returned regardless of the length requested.
294: */
295: public byte[] getBytes(long startPos, int length)
296: throws SQLException {
297: //call checkValidity to exit by throwing a SQLException if
298: //the Blob object has been freed by calling free() on it
299: checkValidity();
300:
301: boolean pushStack = false;
302: try {
303: if (startPos < 1)
304: throw StandardException.newException(
305: SQLState.BLOB_BAD_POSITION, new Long(startPos));
306: if (length < 0)
307: throw StandardException.newException(
308: SQLState.BLOB_NONPOSITIVE_LENGTH, new Integer(
309: length));
310:
311: byte[] result;
312: // if we have a byte array, not a stream
313: if (isBytes) {
314: // if blob length is less than pos bytes + 1, raise an exception
315: if (myBytes.length + 1 < startPos)
316: throw StandardException.newException(
317: SQLState.BLOB_POSITION_TOO_LARGE, new Long(
318: startPos));
319: // cannot go over length of array
320: int lengthFromPos = myBytes.length - (int) startPos + 1;
321: int actualLength = length > lengthFromPos ? lengthFromPos
322: : length;
323: result = new byte[actualLength];
324: System.arraycopy(myBytes, ((int) startPos) - 1, result,
325: 0, actualLength);
326: } else // we have a stream
327: {
328: synchronized (getConnectionSynchronization()) {
329: pushStack = !getEmbedConnection().isClosed();
330: if (pushStack)
331: setupContextStack();
332:
333: setPosition(startPos - 1);
334: // read length bytes into a string
335: result = new byte[length];
336: int n = InputStreamUtil.readLoop(biStream, result,
337: 0, length);
338: pos += n;
339: /*
340: According to the spec, if there are only n < length bytes
341: to return, we should just return these bytes. Rather than
342: return them in an array of size length, where the trailing
343: bytes are not initialized, and the user cannot tell how
344: many bytes were actually returned, we should return an
345: array of n bytes.
346: */
347: if (n < length) {
348: byte[] result2 = new byte[n];
349: System.arraycopy(result, 0, result2, 0, n);
350: return result2;
351: }
352: }
353: }
354: return result;
355: } catch (StandardException e) { // if this is a setPosition exception then we ran out of Blob
356: if (e.getMessageId().equals(SQLState.BLOB_LENGTH_TOO_LONG))
357: e = StandardException.newException(
358: SQLState.BLOB_POSITION_TOO_LARGE, new Long(
359: startPos));
360: throw handleMyExceptions(e);
361: } catch (Throwable t) {
362: throw handleMyExceptions(t);
363: } finally {
364: if (pushStack)
365: restoreContextStack();
366: }
367:
368: }
369:
370: /**
371: * Retrieves the <code>BLOB</code> designated by this
372: * <code>Blob</code> instance as a stream.
373: * @return a stream containing the <code>BLOB</code> data
374: * @exception SQLException if there is an error accessing the
375: * <code>BLOB</code>
376: */
377: public java.io.InputStream getBinaryStream() throws SQLException {
378: //call checkValidity to exit by throwing a SQLException if
379: //the Blob object has been freed by calling free() on it
380: checkValidity();
381:
382: boolean pushStack = false;
383: try {
384: // if we have byte array, not a stream
385: if (isBytes) {
386: return new NewByteArrayInputStream(myBytes);
387: } else {
388: // have a stream
389:
390: synchronized (getConnectionSynchronization()) {
391: pushStack = !getEmbedConnection().isClosed();
392: if (pushStack)
393: setupContextStack();
394:
395: setPosition(0);
396: return biStream;
397: }
398: }
399: } catch (Throwable t) {
400: throw handleMyExceptions(t);
401: } finally {
402: if (pushStack)
403: restoreContextStack();
404: }
405: }
406:
407: /**
408: * Determines the byte position at which the specified byte
409: * <code>pattern</code> begins within the <code>BLOB</code>
410: * value that this <code>Blob</code> object represents. The
411: * search for <code>pattern</code. begins at position
412: * <code>start</code>
413: * @param pattern the byte array for which to search
414: * @param start the position at which to begin searching; the
415: * first position is 1
416: * @return the position at which the pattern appears, else -1.
417: * @exception SQLException if there is an error accessing the
418: * <code>BLOB</code>
419: */
420: public long position(byte[] pattern, long start)
421: throws SQLException {
422: //call checkValidity to exit by throwing a SQLException if
423: //the Blob object has been freed by calling free() on it
424: checkValidity();
425:
426: boolean pushStack = false;
427: try {
428: if (start < 1)
429: throw StandardException.newException(
430: SQLState.BLOB_BAD_POSITION, new Long(start));
431: if (pattern == null)
432: throw StandardException
433: .newException(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR);
434: if (pattern.length == 0)
435: return start; // match DB2's SQL LOCATE function
436:
437: synchronized (getConnectionSynchronization()) {
438: pushStack = !getEmbedConnection().isClosed();
439: if (pushStack)
440: setupContextStack();
441:
442: setPosition(start - 1);
443: // look for first character
444: int lookFor = pattern[0];
445: long curPos;
446: int c;
447: while (true) {
448: c = read();
449: if (c == -1) // run out of stream
450: return -1;
451: if (c == lookFor) {
452: curPos = pos;
453: if (checkMatch(pattern))
454: return curPos;
455: else
456: setPosition(curPos);
457: }
458: }
459: }
460: } catch (StandardException e) { // if this is a setPosition exception then not found
461: if (e.getMessageId().equals(SQLState.BLOB_LENGTH_TOO_LONG))
462: return -1;
463: else
464: throw handleMyExceptions(e);
465: } catch (Throwable t) {
466: throw handleMyExceptions(t);
467: } finally {
468: if (pushStack)
469: restoreContextStack();
470: }
471:
472: }
473:
474: /*
475: check whether pattern (starting from the second byte) appears inside
476: posStream (at the current position)
477: @param posStream the stream to search inside
478: @param pattern the byte array passed in by the user to search with
479: @return true if match, false otherwise
480: */
481: private boolean checkMatch(byte[] pattern) throws IOException {
482: // check whether rest matches
483: // might improve performance by reading more
484: for (int i = 1; i < pattern.length; i++) {
485: int b = read();
486: if ((b < 0) || (b != pattern[i])) // mismatch or stream runs out
487: return false;
488: }
489: return true;
490: }
491:
492: /**
493: * Determines the byte position in the <code>BLOB</code> value
494: * designated by this <code>Blob</code> object at which
495: * <code>pattern</code> begins. The search begins at position
496: * <code>start</code>.
497: * @param pattern the <code>Blob</code> object designating
498: * the <code>BLOB</code> value for which to search
499: * @param start the position in the <code>BLOB</code> value
500: * at which to begin searching; the first position is 1
501: * @return the position at which the pattern begins, else -1
502: * @exception SQLException if there is an error accessing the
503: * <code>BLOB</code>
504: */
505: public long position(Blob pattern, long start) throws SQLException {
506: //call checkValidity to exit by throwing a SQLException if
507: //the Blob object has been freed by calling free() on it
508: checkValidity();
509:
510: boolean pushStack = false;
511: try {
512: if (start < 1)
513: throw StandardException.newException(
514: SQLState.BLOB_BAD_POSITION, new Long(start));
515: if (pattern == null)
516: throw StandardException
517: .newException(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR);
518: synchronized (getConnectionSynchronization()) {
519: pushStack = !getEmbedConnection().isClosed();
520: if (pushStack)
521: setupContextStack();
522:
523: setPosition(start - 1);
524: // look for first character
525: byte[] b;
526: try { // pattern is not necessarily a cloudscape Blob
527: b = pattern.getBytes(1, 1);
528: } catch (SQLException e) {
529: throw StandardException
530: .newException(SQLState.BLOB_UNABLE_TO_READ_PATTERN);
531: }
532: if (b == null || b.length < 1) // the 'empty' blob
533: return start; // match DB2's SQL LOCATE function
534: int lookFor = b[0];
535: int c;
536: long curPos;
537: while (true) {
538: c = read();
539: if (c == -1) // run out of stream
540: return -1;
541: if (c == lookFor) {
542: curPos = pos;
543: if (checkMatch(pattern))
544: return curPos;
545: else
546: setPosition(curPos);
547: }
548: }
549: }
550: } catch (StandardException e) { // if this is a setPosition exception then not found
551: if (e.getMessageId().equals(SQLState.BLOB_LENGTH_TOO_LONG))
552: return -1;
553: else
554: throw handleMyExceptions(e);
555: } catch (Throwable t) {
556: throw handleMyExceptions(t);
557: } finally {
558: if (pushStack)
559: restoreContextStack();
560: }
561:
562: }
563:
564: /*
565: check whether pattern (starting from the second byte) appears inside
566: posStream (at the current position)
567: @param posStream the stream to search inside
568: @param pattern the blob passed in by the user to search with
569: @return true if match, false otherwise
570: */
571: private boolean checkMatch(Blob pattern) throws IOException {
572: // check whether rest matches
573: // might improve performance by reading buffer at a time
574: InputStream pStream;
575: try {
576: pStream = pattern.getBinaryStream();
577: } catch (SQLException e) {
578: return false;
579: }
580: if (pStream == null)
581: return false;
582: // throw away first character since we already read it in the calling
583: // method
584: int b1 = pStream.read();
585: if (b1 < 0)
586: return false;
587: while (true) {
588: b1 = pStream.read();
589: if (b1 < 0) // search blob runs out
590: return true;
591: int b2 = read();
592: if ((b1 != b2) || (b2 < 0)) // mismatch or stream runs out
593: return false;
594: }
595: }
596:
597: /*
598: Convert exceptions where needed before calling handleException to convert
599: them to SQLExceptions.
600: */
601: private SQLException handleMyExceptions(Throwable t)
602: throws SQLException {
603: if (t instanceof StandardException) {
604: // container closed means the blob or clob was accessed after commit
605: if (((StandardException) t).getMessageId().equals(
606: SQLState.DATA_CONTAINER_CLOSED)) {
607: t = StandardException
608: .newException(SQLState.BLOB_ACCESSED_AFTER_COMMIT);
609: }
610: }
611: return handleException(t);
612: }
613:
614: /*
615: If we have a stream, release the resources associated with it.
616: */
617: protected void finalize() {
618: if (!isBytes)
619: ((Resetable) myStream).closeStream();
620: }
621:
622: /**
623: Following methods are for the new JDBC 3.0 methods in java.sql.Blob
624: (see the JDBC 3.0 spec). We have the JDBC 3.0 methods in Local20
625: package, so we don't have to have a new class in Local30.
626: The new JDBC 3.0 methods don't make use of any new JDBC3.0 classes and
627: so this will work fine in jdbc2.0 configuration.
628: */
629:
630: /////////////////////////////////////////////////////////////////////////
631: //
632: // JDBC 3.0 - New public methods
633: //
634: /////////////////////////////////////////////////////////////////////////
635: /**
636: * JDBC 3.0
637: *
638: * Writes the given array of bytes to the BLOB value that this Blob object
639: * represents, starting at position pos, and returns the number of bytes written.
640: *
641: * @param pos - the position in the BLOB object at which to start writing
642: * @param bytes - the array of bytes to be written to the BLOB value that this
643: * Blob object represents
644: * @return the number of bytes written
645: * @exception SQLException Feature not implemented for now.
646: */
647: public int setBytes(long pos, byte[] bytes) throws SQLException {
648: throw Util.notImplemented();
649: }
650:
651: /**
652: * JDBC 3.0
653: *
654: * Writes all or part of the given array of byte array to the BLOB value that
655: * this Blob object represents and returns the number of bytes written.
656: * Writing starts at position pos in the BLOB value; len bytes from the given
657: * byte array are written.
658: *
659: * @param pos - the position in the BLOB object at which to start writing
660: * @param bytes - the array of bytes to be written to the BLOB value that this
661: * Blob object represents
662: * @param offset - the offset into the array bytes at which to start reading
663: * the bytes to be set
664: * @param len - the number of bytes to be written to the BLOB value from the
665: * array of bytes bytes
666: * @return the number of bytes written
667: * @exception SQLException Feature not implemented for now.
668: */
669: public int setBytes(long pos, byte[] bytes, int offset, int len)
670: throws SQLException {
671: throw Util.notImplemented();
672: }
673:
674: /**
675: * JDBC 3.0
676: *
677: * Retrieves a stream that can be used to write to the BLOB value that this
678: * Blob object represents. The stream begins at position pos.
679: *
680: * @param pos - the position in the BLOB object at which to start writing
681: * @return a java.io.OutputStream object to which data can be written
682: * @exception SQLException Feature not implemented for now.
683: */
684: public java.io.OutputStream setBinaryStream(long pos)
685: throws SQLException {
686: throw Util.notImplemented();
687: }
688:
689: /**
690: * JDBC 3.0
691: *
692: * Truncates the BLOB value that this Blob object represents to be len bytes
693: * in length.
694: *
695: * @param len - the length, in bytes, to which the BLOB value that this Blob
696: * object represents should be truncated
697: * @exception SQLException Feature not implemented for now.
698: */
699: public void truncate(long len) throws SQLException {
700: throw Util.notImplemented();
701: }
702:
703: /////////////////////////////////////////////////////////////////////////
704: //
705: // JDBC 4.0 - New public methods
706: //
707: /////////////////////////////////////////////////////////////////////////
708: /**
709: * This method frees the <code>Blob</code> object and releases the resources that
710: * it holds. The object is invalid once the <code>free</code>
711: * method is called. If <code>free</code> is called multiple times, the subsequent
712: * calls to <code>free</code> are treated as a no-op.
713: *
714: * @throws SQLException if an error occurs releasing
715: * the Blob's resources
716: */
717: public void free() throws SQLException {
718: //calling free() on a already freed object is treated as a no-op
719: if (!isValid)
720: return;
721:
722: //now that free has been called the Blob object is no longer
723: //valid
724: isValid = false;
725:
726: //initialialize length to default value -1
727: myLength = -1;
728:
729: //if it is a stream then close it.
730: //if a array of bytes then initialize it to null
731: //to free up space
732: if (!isBytes)
733: ((Resetable) myStream).closeStream();
734: else
735: myBytes = null;
736: }
737:
738: /**
739: * Returns an <code>InputStream</code> object that contains a partial
740: * <code>Blob</code> value, starting with the byte specified by pos,
741: * which is length bytes in length.
742: *
743: * @param pos the offset to the first byte of the partial value to be
744: * retrieved. The first byte in the <code>Blob</code> is at
745: * position 1
746: * @param length the length in bytes of the partial value to be retrieved
747: * @return through which the partial <code>Blob</code> value can be read.
748: * @throws SQLException if pos is less than 1 or if pos is greater than
749: * the number of bytes in the <code>Blob</code> or if pos + length is
750: * greater than the number of bytes in the <code>Blob</code>
751: */
752: public InputStream getBinaryStream(long pos, long length)
753: throws SQLException {
754: throw Util.notImplemented();
755: }
756:
757: /*
758: * Checks is isValid is true. If it is not true throws
759: * a SQLException stating that a method has been called on
760: * an invalid LOB object
761: *
762: * throws SQLException if isvalid is not true.
763: */
764: private void checkValidity() throws SQLException {
765: if (!isValid)
766: throw newSQLException(SQLState.LOB_OBJECT_INVALID);
767: }
768: }
|