You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by kr...@apache.org on 2008/08/05 19:11:33 UTC

svn commit: r682803 - in /db/derby/code/branches/10.4/java: engine/org/apache/derby/impl/jdbc/ testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/

Author: kristwaa
Date: Tue Aug  5 10:11:32 2008
New Revision: 682803

URL: http://svn.apache.org/viewvc?rev=682803&view=rev
Log:
DERBY-3766, DERBY-3783 (second patch): EmbedBlob.setPosition is highly ineffective for streams.
Backporting from trunk: 677619 (derby-3766-1a-preparations.diff), 681359 (derby-3766-2a-position_fix.diff), and 678388 (d3783-initCause.diff).

Modified:
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/AutoPositioningStream.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUpdatableReader.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UpdatableBlobStream.java
    db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/Util.java
    db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/AutoPositioningStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/AutoPositioningStream.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/AutoPositioningStream.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/AutoPositioningStream.java Tue Aug  5 10:11:32 2008
@@ -156,9 +156,7 @@
             }
         }
         catch (StandardException se) {
-            IOException ioe = new IOException (se.getMessage());
-            ioe.initCause (se);
-            throw ioe;
+            throw Util.newIOException(se);
         }
     }
 }

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUpdatableReader.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUpdatableReader.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUpdatableReader.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUpdatableReader.java Tue Aug  5 10:11:32 2008
@@ -222,9 +222,7 @@
                     stream = clob.getInternalClob().getRawByteStream();
                 }
                 catch (SQLException e) {
-                    IOException ioe = new IOException (e.getMessage());
-                    ioe.initCause (e);
-                    throw ioe;
+                    throw Util.newIOException(e);
                 }
                 init ((LOBInputStream) stream, pos);
                 materialized = true;

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java Tue Aug  5 10:11:32 2008
@@ -104,9 +104,7 @@
                 pos += ret;
         }
         catch (SQLException e) {
-            IOException ioe = new IOException (e.getMessage());
-            ioe.initCause (e);
-            throw ioe;
+            throw Util.newIOException(e);
         }
     }
 }

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java Tue Aug  5 10:11:32 2008
@@ -33,6 +33,7 @@
 
 import java.sql.SQLException;
 import java.sql.Blob;
+import java.io.EOFException;
 import java.io.InputStream;
 import java.io.IOException;
 
@@ -78,7 +79,12 @@
      * Derby store, and is read-only.
      */
     private boolean         materialized;
-    private InputStream     myStream;
+    /**
+     * The underlying positionable store stream, if any.
+     * <p>
+     * If {@link #materialized} is {@code true}, the stream is {@code null}.
+     */
+    private PositionedStoreStream myStream;
     
     /**
      * Locator value for this Blob, used as a handle by the client driver to
@@ -91,21 +97,22 @@
     /**
      * Length of the stream representing the Blob.
      * <p>
-     * Set to -1 when the stream has been materialized {@link #materialized} or
+     * Set to -1 when the stream has been {@link #materialized} or
      * the length of the stream is not currently known.
      */
     private long streamLength = -1;
     
-    // 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[];
-    
+    /**
+     * Position offset for the stream representing the Blob, if any.
+     * <p>
+     * This offset accounts for the bytes encoding the stream length at the
+     * head of the stream. Data byte {@code pos} is at
+     * {@code pos + streamPositionOffset} in the underlying stream.
+     * Set to {@code Integer.MIN_VALUE} if the Blob isn't represented by a
+     * store stream.
+     */
+    private final int streamPositionOffset;
+
     //This boolean variable indicates whether the Blob object has
     //been invalidated by calling free() on it
     private boolean isValid = true;
@@ -128,6 +135,7 @@
          try {
              control = new LOBStreamControl (con.getDBName(), blobBytes);
              materialized = true;
+             streamPositionOffset = Integer.MIN_VALUE;
              //add entry in connection so it can be cleared 
              //when transaction is not valid
              con.addLOBReference (this);
@@ -152,10 +160,11 @@
         if (SanityManager.DEBUG)
             SanityManager.ASSERT(!dvd.isNull(), "blob is created on top of a null column");
 
-        myStream = dvd.getStream();
-        if (myStream == null)
+        InputStream dvdStream = dvd.getStream();
+        if (dvdStream == null)
         {
             materialized = true;
+            streamPositionOffset = Integer.MIN_VALUE;
             // copy bytes into memory so that blob can live after result set
             // is closed
             byte[] dvdBytes = dvd.getBytes();
@@ -182,12 +191,14 @@
              implementing the getStream() method for dvd.getStream(), does not
              guarantee this for us
              */
-            if (SanityManager.DEBUG)
-                SanityManager.ASSERT(myStream instanceof Resetable);
-            //make myStream a position aware stream
-            myStream = new PositionedStoreStream (myStream);
+            if (SanityManager.DEBUG) {
+                SanityManager.ASSERT(dvdStream instanceof Resetable);
+            }
+            // Create a position aware stream on top of dvdStream so we can
+            // more easily move back and forth in the Blob.
+            myStream = new PositionedStoreStream(dvdStream);
             try {
-                ((Resetable) myStream).initStream();
+                myStream.initStream();
             } catch (StandardException se) {
                 if (se.getMessageId().equals(SQLState.DATA_CONTAINER_CLOSED)) {
                     throw StandardException
@@ -196,72 +207,97 @@
                     throw se;
                 }
             }
-            // 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];
+            try {
+                // The BinaryToRawStream will read the encoded length bytes.
+                BinaryToRawStream tmpStream =
+                        new BinaryToRawStream(myStream, con);
+                streamPositionOffset = (int)myStream.getPosition();
+                // Check up front if the stream length is specified.
+                streamLength = tmpStream.getLength();
+                tmpStream.close();
+            } catch (IOException ioe) {
+                throw StandardException.newException(
+                        SQLState.LANG_STREAMING_COLUMN_I_O_EXCEPTION, ioe);
+            }
         }
-        pos = 0;
         //add entry in connection so it can be cleared 
         //when transaction is not valid
         con.addLOBReference (this);
     }
 
 
-    /*
-        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)
+    /**
+     * Sets the position of the Blob to {@code logicalPos}, where position 0 is
+     * the beginning of the Blob content.
+     * <p>
+     * The position is only guaranteed to be valid from the time this method is
+     * invoked until the synchronization monitor is released, or until the next
+     * invokation of this method.
+     * <p>
+     * The position is logical in the sense that it specifies the requested
+     * position in the Blob content. This position might be at a different
+     * position in the underlying representation, for instance the Derby store
+     * stream prepends the Blob content with a length field.
+     *
+     * @param logicalPos requested Blob position, 0-based
+     * @return The new position, which will be equal to the requested position.
+     * @throws IOException if reading/accessing the Blob fails
+     * @throws StandardException throws BLOB_POSITION_TOO_LARGE if the requested
+     *      position is larger than the Blob length, throws other SQL states if
+     *      resetting the stream fails
+     */
+    //@GuardedBy(getConnectionSynchronization())
+    private long setBlobPosition(long logicalPos)
         throws StandardException, IOException
     {
         if (SanityManager.DEBUG)
-            SanityManager.ASSERT(newPos >= 0);
-        if (materialized)
-            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_LENGTH_TOO_LONG);
-                pos += size;
+            SanityManager.ASSERT(logicalPos >= 0);
+        if (materialized) {
+            // Nothing to do here, except checking if the position is valid.
+            if (logicalPos >= control.getLength()) {
+                throw StandardException.newException(
+                        SQLState.BLOB_POSITION_TOO_LARGE, new Long(logicalPos));
+            }
+        } else {
+            // Reposition the store stream, account for the length field offset.
+            try {
+                this.myStream.reposition(
+                        logicalPos + this.streamPositionOffset);
+            } catch (EOFException eofe) {
+                throw StandardException.newException(
+                        SQLState.BLOB_POSITION_TOO_LARGE, eofe,
+                        new Long(logicalPos));
             }
         }
+        return logicalPos;
     }
 
 
-    /*
-        Reads one byte, either from the byte array or else from the stream.
-    */
-    private int read() throws IOException, SQLException {
+    /**
+     * Reads one byte from the Blob at the specified position.
+     * <p>
+     * Depending on the representation, this might result in a read from a byte
+     * array, a temporary file on disk or from a Derby store stream.
+     *
+     * @param pos the 0-based position in the Blob to read
+     * @return The byte at the specified position.
+     * @throws IOException if reading from the underlying data representation
+     *      fails
+     */
+    private int read(long pos)
+            throws IOException, StandardException {
         int c;
-        if (materialized)
-        {
-            try {
-                if (pos >= control.getLength())
-                    return -1;
-                else
-                    c = control.read (pos);
-            }
-            catch (StandardException se) {
-                throw Util.generateCsSQLException (se);
-            }
+        if (materialized) {
+            if (pos >= control.getLength())
+                return -1;
+            else
+                c = control.read (pos);
+        } else {
+            // Make sure we're at the right position.
+            this.myStream.reposition(pos + this.streamPositionOffset);
+            // Read one byte from the stream.
+            c = this.myStream.read();
         }
-        else
-            c = biStream.read();
-        pos++;
         return c;
     }
 
@@ -299,35 +335,34 @@
                 if (pushStack)
                     setupContextStack();
 
-                setPosition(0);
-                // If possible get the length from the encoded
-                // length at the front of the raw stream.
-                if ((streamLength = biStream.getLength()) != -1) {
-                    biStream.close();
-                   return streamLength;
+                // We have to read the entire stream!
+                myStream.resetStream();
+                BinaryToRawStream tmpStream =
+                        new BinaryToRawStream(myStream, this);
+                streamLength = 0;
+                if (SanityManager.DEBUG) {
+                    SanityManager.ASSERT(tmpStream.getLength() == -1);
                 }
-                
-                // Otherwise have to read the entire stream!
+
                 for (;;)
                 {
-                    long skipped = biStream.skip(Limits.DB2_LOB_MAXWIDTH);
+                    long skipped = tmpStream.skip(Limits.DB2_LOB_MAXWIDTH);
                     if (SanityManager.DEBUG) {
                         SanityManager.ASSERT(skipped >= 0);
                     }
-                    pos += skipped;
+                    streamLength += skipped;
                     // If skip reports zero bytes skipped, verify EOF.
                     if (skipped == 0) {
-                        if (biStream.read() == -1) {
+                        if (tmpStream.read() == -1) {
                             break; // Exit the loop, no more data.
                         } else {
-                            pos++;
+                            streamLength++;
                         }
                     }
                 }
+                tmpStream.close();
                 // Save for future uses.
-                streamLength = pos;
-                biStream.close();
-                return pos;
+                return streamLength;
             }
         }
         catch (Throwable t)
@@ -403,11 +438,10 @@
                     if (pushStack)
                         setupContextStack();
 
-                    setPosition(startPos-1);
+                    setBlobPosition(startPos-1);
                     // read length bytes into a string
                     result = new byte[length];
-                    int n = InputStreamUtil.readLoop(biStream,result,0,length);
-                    pos += n;
+                    int n = InputStreamUtil.readLoop(myStream,result,0,length);
                     /*
                      According to the spec, if there are only n < length bytes
                      to return, we should just return these bytes. Rather than
@@ -478,7 +512,9 @@
                     if (pushStack)
                         setupContextStack();
 
-                    ((Resetable)myStream).resetStream();
+                    // Reset stream, because AutoPositionigStream wants to read
+                    // the encoded length bytes.
+                    myStream.resetStream();
                     return new UpdatableBlobStream (this, 
                             new AutoPositioningStream (this, myStream, this));
                 }
@@ -533,33 +569,29 @@
                 if (pushStack)
                     setupContextStack();
 
-                setPosition(start-1);
+                long pos = setBlobPosition(start -1);
                 // look for first character
                 int lookFor = pattern[0];
                 long curPos;
                 int c;
                 while (true)
                 {
-                    c = read();
+                    c = read(pos++); // Note the position increment.
                     if (c == -1)  // run out of stream
                         return -1;
                     if (c == lookFor)
                     {
                         curPos = pos;
-                        if (checkMatch(pattern))
+                        if (checkMatch(pattern, pos))
                             return curPos;
                         else
-                            setPosition(curPos);
+                            pos = setBlobPosition(curPos);
                     }
                 }
             }
         }
-        catch (StandardException e)
-        {  // if this is a setPosition exception then not found
-            if (e.getMessageId().equals(SQLState.BLOB_LENGTH_TOO_LONG))
-                return -1;
-            else
-                throw handleMyExceptions(e);
+        catch (StandardException e) {
+            throw handleMyExceptions(e);
         }
         catch (Throwable t)
         {
@@ -573,21 +605,24 @@
 
     }
 
-
-    /*
-     check whether pattern (starting from the second byte) appears inside
-     posStream (at the current position)
-     @param posStream the stream to search inside
-     @param pattern the byte array passed in by the user to search with
-     @return true if match, false otherwise
+    /**
+     * Checks if the pattern (starting from the second byte) appears inside
+     * the Blob content.
+     * <p>
+     * At this point, the first byte of the pattern must already have been
+     * matched, and {@code pos} must be pointing at the second byte to compare.
+     *
+     * @param pattern the byte array to search for, passed in by the user
+     * @param pos the position in the Blob content to start searching from
+     * @return {@code true} if a match is found, {@code false} if not.
      */
-    private boolean checkMatch(byte[] pattern)
-        throws IOException, SQLException {
+    private boolean checkMatch(byte[] pattern, long pos)
+            throws IOException, StandardException {
        // check whether rest matches
        // might improve performance by reading more
         for (int i = 1; i < pattern.length; i++)
         {
-            int b = read();
+            int b = read(pos++);
             if ((b < 0) || (b != pattern[i]))  // mismatch or stream runs out
                 return false;
         }
@@ -628,7 +663,7 @@
                 if (pushStack)
                     setupContextStack();
 
-                setPosition(start-1);
+                long pos = setBlobPosition(start-1);
                 // look for first character
                 byte[] b;
                 try
@@ -646,26 +681,22 @@
                 long curPos;
                 while (true)
                 {
-                    c = read();
+                    c = read(pos++); // Note the position increment.
                     if (c == -1)  // run out of stream
                         return -1;
                     if (c == lookFor)
                     {
                         curPos = pos;
-                        if (checkMatch(pattern))
+                        if (checkMatch(pattern, pos))
                             return curPos;
                         else
-                            setPosition(curPos);
+                            pos = setBlobPosition(curPos);
                     }
                 }
             }
         }
-        catch (StandardException e)
-        {  // if this is a setPosition exception then not found
-            if (e.getMessageId().equals(SQLState.BLOB_LENGTH_TOO_LONG))
-                return -1;
-            else
-                throw handleMyExceptions(e);
+        catch (StandardException e) {
+            throw handleMyExceptions(e);
         }
         catch (Throwable t)
         {
@@ -680,16 +711,16 @@
     }
 
 
-    /*
-     check whether pattern (starting from the second byte) appears inside
-     posStream (at the current position)
-     @param posStream the stream to search inside
-     @param pattern the blob passed in by the user to search with
-     @return true if match, false otherwise
+    /**
+     * Checks if the pattern (starting from the second byte) appears inside
+     * the Blob content.
+     *
+     * @param pattern the Blob to search for, passed in by the user
+     * @param pos the position in the Blob (this) content to start searching
+     * @return {@code true} if a match is found, {@code false} if not.
      */
-    private boolean checkMatch(Blob pattern)
-        throws IOException, SQLException
-    {
+    private boolean checkMatch(Blob pattern, long pos)
+            throws IOException, StandardException {
         // check whether rest matches
         // might improve performance by reading buffer at a time
         InputStream pStream;
@@ -713,7 +744,7 @@
             b1 = pStream.read();
             if (b1 < 0)  // search blob runs out
                 return true;
-            int b2 = read();
+            int b2 = read(pos++);
             if ((b1 != b2) || (b2 < 0))  // mismatch or stream runs out
                 return false;
         }
@@ -744,7 +775,7 @@
     protected void finalize()
     {
         if (!materialized)
-            ((Resetable)myStream).closeStream();
+            myStream.closeStream();
     }
 
 	/**
@@ -885,7 +916,7 @@
 	{
             if (len > length())
                 throw Util.generateCsSQLException(
-                    SQLState.BLOB_LENGTH_TOO_LONG, new Long(pos));
+                    SQLState.BLOB_LENGTH_TOO_LONG, new Long(len));
             try {
                 if (materialized) {
                     control.truncate (len);
@@ -939,9 +970,10 @@
         //if it is a stream then close it.
         //if a array of bytes then initialize it to null
         //to free up space
-        if (!materialized)
-            ((Resetable)myStream).closeStream();
-        else {
+        if (!materialized) {
+            myStream.closeStream();
+            myStream = null;
+        } else {
             try {
                 control.free ();
                 control = null;

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java Tue Aug  5 10:11:32 2008
@@ -131,7 +131,7 @@
                                             SQLState.BLOB_INVALID_OFFSET))) {
                 throw new ArrayIndexOutOfBoundsException(se.getMessage());
             } else {
-                throw new IOException(se.getMessage());
+                throw Util.newIOException(se);
             }
         }
     }
@@ -173,7 +173,7 @@
                 pos += 1;
             return ret;
         } catch (StandardException se) {
-            throw new IOException (se.getMessage());
+            throw Util.newIOException(se);
         }
     }
 

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java Tue Aug  5 10:11:32 2008
@@ -68,7 +68,7 @@
         try {
             pos = control.write(b, pos);
         } catch (StandardException se) {
-            throw new IOException (se.getMessage());
+            throw Util.newIOException(se);
         }
     }
 
@@ -113,7 +113,7 @@
                                             SQLState.BLOB_INVALID_OFFSET))) {
                 throw new ArrayIndexOutOfBoundsException(se.getMessage());
             }
-            throw new IOException (se.getMessage());
+            throw Util.newIOException(se);
         }
     }
 

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java Tue Aug  5 10:11:32 2008
@@ -114,9 +114,7 @@
                 throw (StandardException)e;
             if (e instanceof IOException)
                 throw (IOException) e;
-            IOException ioe = new IOException (e.getMessage());
-            ioe.initCause (e);
-            throw ioe;
+            throw Util.newIOException(e);
         }
         isBytes = false;
         //now this call will write into the file
@@ -397,9 +395,7 @@
                 throw (IOException) e;
             if (e instanceof RuntimeException)
                 throw (RuntimeException) e;
-            IOException ioe = new IOException(e.getMessage());
-            ioe.initCause(e);
-            throw ioe;
+            throw Util.newIOException(e);
         }
     }
     /**

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java Tue Aug  5 10:11:32 2008
@@ -142,9 +142,7 @@
                     try {
                         this.positionedIn.resetStream();
                     } catch (StandardException se) {
-                        IOException ioe = new IOException(se.getMessage());
-                        ioe.initCause(se);
-                        throw ioe;
+                        throw Util.newIOException(se);
                     }
                 } else {
                     this.positionedIn = null;
@@ -567,10 +565,7 @@
             parent.restoreContextStack();
         }
         } catch (SQLException sqle) {
-            IOException ioe =
-                new IOException(sqle.getSQLState() + ": " + sqle.getMessage());
-            ioe.initCause(sqle);
-            throw ioe;
+            throw Util.newIOException(sqle);
         }
     }
 

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UpdatableBlobStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UpdatableBlobStream.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UpdatableBlobStream.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/UpdatableBlobStream.java Tue Aug  5 10:11:32 2008
@@ -110,9 +110,7 @@
             try {
                 stream = blob.getBinaryStream();
             } catch (SQLException ex) {
-                IOException ioe = new IOException (ex.getMessage());
-                ioe.initCause (ex);
-                throw ioe;
+                throw Util.newIOException(ex);
             }
             long leftToSkip = pos;
             while (leftToSkip > 0) {

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/Util.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/Util.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/Util.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/Util.java Tue Aug  5 10:11:32 2008
@@ -282,6 +282,18 @@
 	}
 
     /**
+     * Create an {@code IOException} that wraps another {@code Throwable}.
+     *
+     * @param cause the underlying cause of the error
+     * @return an {@code IOException} linked to {@code cause}
+     */
+    static IOException newIOException(Throwable cause) {
+        IOException ioe = new IOException(cause.getMessage());
+        ioe.initCause(cause);
+        return ioe;
+    }
+
+    /**
      * this method is called to replace the exception factory to be 
      * used to generate the SQLException or the subclass
      */

Modified: db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java?rev=682803&r1=682802&r2=682803&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java (original)
+++ db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java Tue Aug  5 10:11:32 2008
@@ -21,6 +21,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.CharArrayReader;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
@@ -42,6 +43,7 @@
 import junit.framework.*;
 import java.sql.*;
 
+import org.apache.derbyTesting.functionTests.util.streams.ByteAlphabet;
 import org.apache.derbyTesting.junit.TestConfiguration;
 
 /**
@@ -1866,6 +1868,76 @@
     }
 
     /**
+     * Tests the {@code Blob.position} using a deterministic sequence of
+     * actions and arguments.
+     */
+    public void testPositionBlobDeterministic()
+            throws IOException, SQLException {
+        getConnection().setAutoCommit(false);
+        final int size = 100000;
+        PreparedStatement ps = prepareStatement(
+                "INSERT INTO testBlob (a, b) VALUES (?, ?)");
+        ps.setBinaryStream(1, new LoopingAlphabetStream(size), size);
+        ps.setInt(2, size);
+        ps.executeUpdate();
+        ps.close();
+        ps = prepareStatement("select a from testBlob where b = ?");
+        ps.setInt(1, size);
+        ResultSet rs = ps.executeQuery();
+        assertTrue("No data found", rs.next());
+        Blob blob = rs.getBlob(1);
+        // Try with a one-byte pattern.
+        byte[] pattern = new byte[] {(byte)'k'}; // k number 11 in the alphabet
+        assertEquals(11, blob.position(pattern, 1));
+        // Try with a non-existing pattern.
+        pattern = new byte[] {(byte)'p', (byte)'o'};
+        assertEquals(-1, blob.position(pattern, size / 3));
+
+        // Loop through all matches 
+        pattern = new byte[] {(byte)'d', (byte)'e'};
+        long foundAtPos = 1;
+        int index = 0;
+        int stepSize = ByteAlphabet.modernLatinLowercase().byteCount();
+        while ((foundAtPos = blob.position(pattern, foundAtPos +1)) != -1) {
+            assertEquals((stepSize * index++) + 4, foundAtPos);
+            byte[] fetchedPattern = blob.getBytes(foundAtPos, pattern.length);
+            assertTrue(Arrays.equals(pattern, fetchedPattern));
+        }
+
+        // Try a longer pattern.
+        int pSize = 65*1024; // 65 KB
+        pattern = new byte[pSize];
+        assertEquals(pSize, new LoopingAlphabetStream(pSize).read(pattern));
+        assertEquals(1, blob.position(pattern, 1));
+        assertEquals(stepSize * 100 +1,
+                blob.position(pattern, stepSize * 99 + 7));
+        // Try again after getting the length.
+        assertEquals(size, blob.length());
+        assertEquals(stepSize * 100 +1,
+                blob.position(pattern, stepSize * 99 + 7));
+
+        // Try specifing a starting position that's too big.
+        try {
+            blob.position(pattern, size*2);
+            fail("Accepted position after end of Blob");
+        } catch (SQLException sqle) {
+            assertSQLState("XJ076", sqle);
+        }
+
+        // Fetch the last 5 bytes, try with a partial match at the end.
+        byte[] blobEnd = blob.getBytes(size - 4, 5);
+        pattern = new byte[6];
+        System.arraycopy(blobEnd, 0, pattern, 0, blobEnd.length);
+        pattern[5] = 'X'; // Only lowercase in the looping alphabet stream.
+        assertEquals(-1, blob.position(pattern, size - 10));
+
+        // Get the very last byte, try with a partial match at the end.
+        blobEnd = blob.getBytes(size, 1);
+        pattern = new byte[] {blobEnd[0], 'X'};
+        assertEquals(-1, blob.position(pattern, size - 5));
+    }
+
+    /**
      * Test Blob.position() with blob argument
      */
     public void testPositionBlob() throws Exception {