📄 embedblob.java
字号:
/* Derby - Class org.apache.derby.impl.jdbc.EmbedBlob Copyright 2000, 2004 The Apache Software Foundation or its licensors, as applicable. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */package org.apache.derby.impl.jdbc;import org.apache.derby.iapi.reference.SQLState;import org.apache.derby.iapi.error.StandardException;import org.apache.derby.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.types.DataValueDescriptor;import org.apache.derby.iapi.types.Resetable;import org.apache.derby.impl.jdbc.ConnectionChild;import org.apache.derby.impl.jdbc.EmbedConnection;import org.apache.derby.impl.jdbc.Util;import org.apache.derby.iapi.services.io.NewByteArrayInputStream;import org.apache.derby.iapi.services.io.InputStreamUtil;import org.apache.derby.iapi.services.io.ArrayInputStream;import java.sql.SQLException;import java.sql.Blob;import java.io.InputStream;import java.io.EOFException;import java.io.IOException;/** Implements java.sql.Blob (see the JDBC 2.0 spec). A blob sits on top of a BINARY, VARBINARY or LONG VARBINARY column. If its data is small (less than 1 page) it is a byte array taken from the SQLBit class. If it is large (more than 1 page) it is a long column in the database. The long column is accessed as a stream, and is implemented in store as an OverflowInputStream. The Resetable interface allows sending messages to that stream to initialize itself (reopen its container and lock the corresponding row) and to reset itself to the beginning. NOTE: In the case that the data is large, it is represented as a stream. This stream is returned to the user in the getBinaryStream() method. This means that we have limited control over the state of the stream, since the user can read bytes from it at any time. Thus all methods here reset the stream to the beginning before doing any work. CAVEAT: The methods may not behave correctly if a user sets up multiple threads and sucks data from the stream (returned from getBinaryStream()) at the same time as calling the Blob methods. <P><B>Supports</B> <UL> <LI> JSR169 - no subsetting for java.sql.Blob <LI> JDBC 2.0 <LI> JDBC 3.0 - no new dependencies on new JDBC 3.0 or JDK 1.4 classes, new update methods can safely be added into implementation. </UL> */final class EmbedBlob extends ConnectionChild implements Blob{ // clob is either bytes or stream private boolean isBytes; private InputStream myStream; private byte[] myBytes; // note: cannot control position of the stream since user can do a getBinaryStream private long pos; // this stream sits on top of myStream private BinaryToRawStream biStream; // buffer for reading in blobs from a stream (long column) // and trashing them (to set the position of the stream etc.) private static int BLOB_BUF_SIZE = 4096; private byte buf[]; /* This constructor should only be called by EmbedResultSet.getBlob */ protected EmbedBlob(DataValueDescriptor dvd, EmbedConnection con) throws StandardException { super(con); // if the underlying column is null, ResultSet.getBlob will return null, // never should get this far if (SanityManager.DEBUG) SanityManager.ASSERT(!dvd.isNull(), "blob is created on top of a null column"); myStream = dvd.getStream(); if (myStream == null) { isBytes = true; // copy bytes into memory so that blob can live after result set // is closed byte[] dvdBytes = dvd.getBytes(); if (SanityManager.DEBUG) SanityManager.ASSERT(dvdBytes != null,"blob has a null value underneath"); myBytes = new byte[dvdBytes.length]; System.arraycopy(dvdBytes, 0, myBytes, 0, dvdBytes.length); } else { isBytes = false; /* We are expecting this stream to be a FormatIdInputStream with an OverflowInputStream inside. FormatIdInputStream implements Resetable. This should be the case when retrieving data from a long column. However, SQLBit, which is the class implementing the getStream() method for dvd.getStream(), does not guarantee this for us */ if (SanityManager.DEBUG) SanityManager.ASSERT(myStream instanceof Resetable); try { ((Resetable) myStream).initStream(); } catch (StandardException se) { if (se.getMessageId().equals(SQLState.DATA_CONTAINER_CLOSED)) { throw StandardException .newException(SQLState.BLOB_ACCESSED_AFTER_COMMIT); } } // set up the buffer for trashing the bytes to set the position of // the // stream, only need a buffer when we have a long column buf = new byte[BLOB_BUF_SIZE]; } pos = 0; } /* Sets the position of the stream to position newPos, where position 0 is the beginning of the stream. @param newPos the position to set to @exception StandardException (BLOB_SETPOSITION_FAILED) throws this if the stream runs out before we get to newPos */ private void setPosition(long newPos) throws StandardException, IOException { if (SanityManager.DEBUG) SanityManager.ASSERT(newPos >= 0); if (isBytes) pos = newPos; else { // Always resets the stream to the beginning first, because user can // influence the state of the stream without letting us know. ((Resetable)myStream).resetStream(); // PT could try to save creating a new object each time biStream = new BinaryToRawStream(myStream, this); pos = 0; while (pos < newPos) { int size = biStream.read( buf,0,(int) Math.min((newPos-pos), (long) BLOB_BUF_SIZE)); if (size <= 0) // ran out of stream throw StandardException.newException(SQLState.BLOB_SETPOSITION_FAILED); pos += size; } } } /* Reads one byte, either from the byte array or else from the stream. */ private int read() throws IOException { int c; if (isBytes) { if (pos >= myBytes.length) return -1; else c = myBytes[(int) pos]; } else c = biStream.read(); pos++; return c; } /** * Returns the number of bytes in the <code>BLOB</code> value * designated by this <code>Blob</code> object. * @return length of the <code>BLOB</code> in bytes * @exception SQLException if there is an error accessing the * length of the <code>BLOB</code> */ // PT stream part may get pushed to store public long length() throws SQLException { boolean pushStack = false; try { if (isBytes) return myBytes.length; // we have a stream synchronized (getConnectionSynchronization()) { pushStack = !getEmbedConnection().isClosed(); if (pushStack) setupContextStack(); setPosition(0); for (;;) { int size = biStream.read(buf); if (size == -1) break; pos += size; } return pos; } } catch (Throwable t) { throw handleMyExceptions(t); } finally { if (pushStack) restoreContextStack(); } } /** * Returns as an array of bytes part or all of the <code>BLOB</code> * value that this <code>Blob</code> object designates. The byte * array contains up to <code>length</code> consecutive bytes * starting at position <code>pos</code>. * @param pos the ordinal position of the first byte in the * <code>BLOB</code> value to be extracted; the first byte is at * position 1 * @param length is the number of consecutive bytes to be copied * @return a byte array containing up to <code>length</code> * consecutive bytes from the <code>BLOB</code> value designated * by this <code>Blob</code> object, starting with the * byte at position <code>pos</code>. * @exception SQLException if there is an error accessing the * <code>BLOB</code> NOTE: return new byte[0] if startPos is too large */ // PT stream part may get pushed to store public byte[] getBytes(long startPos, int length) throws SQLException { boolean pushStack = false; try { if (startPos < 1) throw StandardException.newException( SQLState.BLOB_BAD_POSITION, new Long(startPos)); if (length <= 0) throw StandardException.newException( SQLState.BLOB_NONPOSITIVE_LENGTH, new Integer(length)); byte[] result; // if we have a byte array, not a stream if (isBytes) { // if blob length is less than pos bytes, raise an exception if (myBytes.length < startPos) throw StandardException.newException( SQLState.BLOB_POSITION_TOO_LARGE, new Long(startPos)); // cannot go over length of array int lengthFromPos = myBytes.length - (int) startPos + 1; int actualLength = length > lengthFromPos ? lengthFromPos : length; result = new byte[actualLength]; System.arraycopy(myBytes, ((int) startPos) - 1, result, 0, actualLength); } else // we have a stream { synchronized (getConnectionSynchronization()) { pushStack = !getEmbedConnection().isClosed(); if (pushStack) setupContextStack(); setPosition(startPos-1); // read length bytes into a string result = new byte[length]; int n = InputStreamUtil.readLoop(biStream,result,0,length); pos += n; /* According to the spec, if there are only n < length bytes to return, we should just return these bytes. Rather than return them in an array of size length, where the trailing bytes are not initialized, and the user cannot tell how many bytes were actually returned, we should return an array of n bytes. */ if (n < length) { byte[] result2 = new byte[n]; System.arraycopy(result,0,result2,0,n); return result2; } } } return result; } catch (StandardException e) { // if this is a setPosition exception then we ran out of Blob if (e.getMessageId().equals(SQLState.BLOB_SETPOSITION_FAILED)) e = StandardException.newException( SQLState.BLOB_POSITION_TOO_LARGE, new Long(startPos)); throw handleMyExceptions(e); } catch (Throwable t) { throw handleMyExceptions(t); } finally { if (pushStack) restoreContextStack(); } } /** * Retrieves the <code>BLOB</code> designated by this * <code>Blob</code> instance as a stream. * @return a stream containing the <code>BLOB</code> data * @exception SQLException if there is an error accessing the * <code>BLOB</code> */ public java.io.InputStream getBinaryStream() throws SQLException { boolean pushStack = false; try { // if we have byte array, not a stream if (isBytes) { return new NewByteArrayInputStream(myBytes); } else { // have a stream
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -