db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From krist...@apache.org
Subject svn commit: r750409 - in /db/derby/code/branches/10.4/java: client/org/apache/derby/client/am/ engine/org/apache/derby/impl/jdbc/ testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ testing/org/apache/derbyTesting/functionTests/util/streams/
Date Thu, 05 Mar 2009 10:23:19 GMT
Author: kristwaa
Date: Thu Mar  5 10:23:18 2009
New Revision: 750409

URL: http://svn.apache.org/viewvc?rev=750409&view=rev
Log:
DERBY-4060: Blob.getBinaryStream(long,long) is off by one for the pos+len check.
Merged fix and tests from trunk (revision 749235).

Modified:
    db/derby/code/branches/10.4/java/client/org/apache/derby/client/am/Lob.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/EmbedClob.java
    db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobTest.java
    db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java
    db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/CharAlphabet.java
    db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/LoopingAlphabetReader.java

Modified: db/derby/code/branches/10.4/java/client/org/apache/derby/client/am/Lob.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/client/org/apache/derby/client/am/Lob.java?rev=750409&r1=750408&r2=750409&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/client/org/apache/derby/client/am/Lob.java (original)
+++ db/derby/code/branches/10.4/java/client/org/apache/derby/client/am/Lob.java Thu Mar  5
10:23:18 2009
@@ -321,7 +321,7 @@
      *         a) pos <= 0
      *         b) pos > (length of LOB)
      *         c) length < 0
-     *         d) pos + length > (length of LOB)
+     *         d) (pos -1) + length > (length of LOB)
      */
     protected void checkPosAndLength(long pos, long length)
     throws SQLException {
@@ -335,7 +335,7 @@
                 new ClientMessageId(SQLState.BLOB_NONPOSITIVE_LENGTH),
                 new Integer((int)length)).getSQLException();
         }
-        if (length > (this.length() - pos)) {
+        if (length > (this.length() - (pos -1))) {
             throw new SqlException(agent_.logWriter_,
                 new ClientMessageId(SQLState.POS_AND_LENGTH_GREATER_THAN_LOB),
                 new Long(pos), new Long(length)).getSQLException();

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=750409&r1=750408&r2=750409&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 Thu
Mar  5 10:23:18 2009
@@ -1012,7 +1012,7 @@
                     SQLState.BLOB_NONPOSITIVE_LENGTH,
                     new Long(length));
         }
-        if (length > (this.length() - pos)) {
+        if (length > (this.length() - (pos -1))) {
             throw Util.generateCsSQLException(
                     SQLState.POS_AND_LENGTH_GREATER_THAN_LOB,
                     new Long(pos), new Long(length));

Modified: db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java?rev=750409&r1=750408&r2=750409&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java (original)
+++ db/derby/code/branches/10.4/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java Thu
Mar  5 10:23:18 2009
@@ -711,7 +711,7 @@
                     SQLState.BLOB_NONPOSITIVE_LENGTH,
                     new Long(length));
         }
-        if (length > (this.length() - pos)) {
+        if (length > (this.length() - (pos -1))) {
             throw Util.generateCsSQLException(
                     SQLState.POS_AND_LENGTH_GREATER_THAN_LOB,
                     new Long(pos), new Long(length));

Modified: db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobTest.java?rev=750409&r1=750408&r2=750409&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobTest.java
(original)
+++ db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobTest.java
Thu Mar  5 10:23:18 2009
@@ -431,6 +431,69 @@
     }
     
     /**
+     * Obtains a binary stream and tries to drain it to read the last byte in
+     * the Blob.
+     * <p>
+     * See DERBY-4060.
+     *
+     * @throws IOException if reading from a stream fails
+     * @throws SQLException if something goes wrong
+     */
+    public void testGetBinaryStreamLongLastByte()
+            throws IOException, SQLException {
+        int length = 5000;
+        // Insert a Blob
+        PreparedStatement ps = prepareStatement(
+            "insert into BLOBCLOB(ID, BLOBDATA) values(?,?)");
+        int id = BlobClobTestSetup.getID();
+        ps.setInt(1, id);
+        ps.setBinaryStream(2, new LoopingAlphabetStream(length), length);
+        ps.execute();
+        ps.close();
+
+        // Get last byte from the source stream.
+        InputStream cmpIs = new LoopingAlphabetStream(length);
+        cmpIs.skip(length -1);
+        int srcLastByte = cmpIs.read();
+        assertTrue(cmpIs.read() == -1);
+
+        // Read everything first.
+        int bytesToRead = 5000;
+        ps = prepareStatement("select BLOBDATA from BLOBCLOB where ID=?");
+        ps.setInt(1, id);
+        ResultSet rs = ps.executeQuery();
+        rs.next();
+        InputStream is = rs.getBlob(1).getBinaryStream(
+                                        length - bytesToRead +1, bytesToRead);
+
+        // Drain the stream, and make sure we are able to read the last byte.
+        int lastByteRead = getLastByteInStream(is, bytesToRead);
+        assertEquals(srcLastByte, lastByteRead);
+        is.close();
+        rs.close();
+
+        // Read a portion of the stream.
+        bytesToRead = 2000;
+        rs = ps.executeQuery();
+        rs.next();
+        is = rs.getBlob(1).getBinaryStream(
+                                        length - bytesToRead +1, bytesToRead);
+        assertEquals(srcLastByte, lastByteRead);
+        is.close();
+        rs.close();
+
+        // Read a very small portion of the stream.
+        bytesToRead = 1;
+        rs = ps.executeQuery();
+        rs.next();
+        is = rs.getBlob(1).getBinaryStream(
+                                        length - bytesToRead +1, bytesToRead);
+        assertEquals(srcLastByte, lastByteRead);
+        is.close();
+        rs.close();
+    }
+
+    /**
      * Tests the exceptions thrown by the getBinaryStream
      * (long pos, long length) for the following conditions
      * a) pos <= 0
@@ -845,6 +908,42 @@
     }
     
     /**
+     * Drains the stream and returns the last byte read from the stream.
+     *
+     * @param is stream to drain
+     * @param expectedCount expected number of bytes (remaining) in the stream
+     * @return The last byte read.
+     * @throws AssertionError if there are too many/few bytes in the stream
+     * @throws IOException if reading from the stream fails
+     */
+    public static int getLastByteInStream(InputStream is, int expectedCount)
+            throws IOException {
+        int read = 0;
+        byte[] buf = new byte[256];
+        assertTrue(buf.length > 0); // Do not allow an infinite loop here.
+        while (true) {
+            int readThisTime = is.read(buf, 0, buf.length);
+            // -1 is expected, but catch all cases with a negative return value.
+            if (readThisTime < 0) {
+                assertTrue("Invalid return value from stream",
+                        readThisTime == -1);
+                fail("Reached EOF prematurely, expected " + expectedCount +
+                        ", got " + read);
+            } else if (readThisTime == 0) {
+                // Another special case that should not happen.
+                fail("Stream breaks contract, read zero bytes " + is);
+            }
+            read += readThisTime;
+            if (read == expectedCount) {
+                return buf[readThisTime -1];
+            } else if (read > expectedCount) {
+                fail("Too many bytes in stream, expected " + expectedCount +
+                        "have " + read + "(EOF not reached/confirmed)");
+            }
+        }
+    }
+
+    /**
      * Create test suite for this test.
      */
     public static Test suite()

Modified: db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java?rev=750409&r1=750408&r2=750409&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java
(original)
+++ db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java
Thu Mar  5 10:23:18 2009
@@ -44,6 +44,7 @@
  * d) Whether the method is exempted in the NetworkClient
  *
  */
+import org.apache.derbyTesting.functionTests.util.streams.CharAlphabet;
 import org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetReader;
 import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
 class ExemptClobMD {
@@ -416,6 +417,111 @@
     }
 
     /**
+     * Obtains streams from the Clob reading portions of the content, always
+     * including the last character in the Clob.
+     * <p>
+     * This case fills the Clob with latin lowercase characters.
+     */
+    public void testGetCharacterStreamLongLastCharLatin()
+            throws IOException, SQLException {
+        CharAlphabet alphabet = CharAlphabet.modernLatinLowercase();
+        // Insert a Clob
+        int length = 5000;
+        PreparedStatement ps = prepareStatement(
+            "insert into BLOBCLOB(ID, CLOBDATA) values(?,?)");
+        int id = BlobClobTestSetup.getID();
+        ps.setInt(1, id);
+        ps.setCharacterStream(2,
+                new LoopingAlphabetReader(length, alphabet), length);
+        ps.execute();
+        ps.close();
+        // Perform the actual test.
+        getCharacterStreamLongLastChar(id, length, alphabet);
+    }
+
+    /**
+     * Obtains streams from the Clob reading portions of the content, always
+     * including the last character in the Clob.
+     * <p>
+     * This case fills the Clob with Chinese/Japanese/Korean characters.
+     */
+    public void testGetCharacterStreamLongLastCharCJK()
+            throws IOException, SQLException {
+        CharAlphabet alphabet = CharAlphabet.cjkSubset();
+        // Insert a Clob
+        int length = 9001;
+        PreparedStatement ps = prepareStatement(
+            "insert into BLOBCLOB(ID, CLOBDATA) values(?,?)");
+        int id = BlobClobTestSetup.getID();
+        ps.setInt(1, id);
+        ps.setCharacterStream(2,
+                new LoopingAlphabetReader(length, alphabet), length);
+        ps.execute();
+        ps.close();
+        // Perform the actual test.
+        getCharacterStreamLongLastChar(id, length, alphabet);
+    }
+
+    /**
+     * Obtains streams from the Clob and makes sure we can always read the
+     * last char in the Clob.
+     * <p>
+     * See DERBY-4060.
+     *
+     * @param id id of the Clob to use
+     * @param length the length of the Clob
+     * @param alphabet the alphabet used to create the content
+     * @throws IOException if reading from a stream fails
+     * @throws SQLException if something goes wrong
+     */
+    private void getCharacterStreamLongLastChar(int id, int length,
+                                                CharAlphabet alphabet)
+            throws IOException, SQLException {
+        // Get last char from the source stream.
+        Reader cmpReader = new LoopingAlphabetReader(length, alphabet);
+        cmpReader.skip(length -1);
+        char srcLastChar = (char)cmpReader.read();
+        assertTrue(cmpReader.read() == -1);
+
+        PreparedStatement ps = prepareStatement(
+                "select CLOBDATA from BLOBCLOB where ID=?");
+        ps.setInt(1, id);
+        // Read everything first.
+        int charsToRead = length;
+        ResultSet rs = ps.executeQuery();
+        rs.next();
+        Reader reader = rs.getClob(1).getCharacterStream(
+                                        length - charsToRead +1, charsToRead);
+        // Drain the stream, and make sure we are able to read the last char.
+        char lastCharRead = getLastCharInStream(reader, charsToRead);
+        assertEquals(srcLastChar, lastCharRead);
+        reader.close();
+        rs.close();
+
+        // Read a portion of the stream.
+        charsToRead = length / 4;
+        rs = ps.executeQuery();
+        rs.next();
+        reader = rs.getClob(1).getCharacterStream(
+                                        length - charsToRead +1, charsToRead);
+        lastCharRead = getLastCharInStream(reader, charsToRead);
+        assertEquals(srcLastChar, lastCharRead);
+        reader.close();
+        rs.close();
+
+        // Read a very small portion of the stream.
+        charsToRead = 1;
+        rs = ps.executeQuery();
+        rs.next();
+        reader = rs.getClob(1).getCharacterStream(
+                                        length - charsToRead +1, charsToRead);
+        lastCharRead = getLastCharInStream(reader, charsToRead);
+        assertEquals(srcLastChar, lastCharRead);
+        reader.close();
+        rs.close();
+    }
+
+    /**
      * Test that <code>Clob.getCharacterStream(long,long)</code> works on CLOBs
      * that are streamed from store. (DERBY-2891)
      */
@@ -837,6 +943,42 @@
 
     
     /**
+     * Drains the stream and returns the last char read from the stream.
+     *
+     * @param reader stream to drain
+     * @param expectedCount expected number of chars (remaining) in the stream
+     * @return The last char read.
+     * @throws AssertionError if there are too many/few chars in the stream
+     * @throws IOException if reading from the stream fails
+     */
+    public static char getLastCharInStream(Reader reader, int expectedCount)
+            throws IOException {
+        int read = 0;
+        final char[] buf = new char[256];
+        assertTrue(buf.length > 0); // Do not allow an infinite loop here.
+        while (true) {
+            int readThisTime = reader.read(buf, 0, buf.length);
+            // -1 is expected, but catch all cases with a negative return value.
+            if (readThisTime < 0) {
+                assertEquals("Invalid return value from stream",
+                        -1, readThisTime);
+                fail("Reached EOF prematurely, expected " + expectedCount +
+                        ", got " + read);
+            } else if (readThisTime == 0) {
+                // Another special case that should not happen.
+                fail("Stream breaks contract, read zero chars: " + reader);
+            }
+            read += readThisTime;
+            if (read == expectedCount) {
+                return buf[readThisTime -1];
+            } else if (read > expectedCount) {
+                fail("Too many chars in stream, expected " + expectedCount +
+                        "have " + read + "(EOF not reached/confirmed)");
+            }
+        }
+    }
+
+    /**
      * Create test suite for this test.
      */
     public static Test suite()

Modified: db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/CharAlphabet.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/CharAlphabet.java?rev=750409&r1=750408&r2=750409&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/CharAlphabet.java
(original)
+++ db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/CharAlphabet.java
Thu Mar  5 10:23:18 2009
@@ -173,4 +173,21 @@
     public void reset() {
         off = 0;
     }
+
+    /**
+     * Returns a clone of the alphabet.
+     *
+     * @return A clone.
+     */
+    public CharAlphabet getClone() {
+        return new CharAlphabet(name, chars);
+    }
+
+    /**
+     * Returns a friendlier textual representation of the alphabet.
+     */
+    public String toString() {
+        return (name + "@" + hashCode() + "(charCount=" + charCount + ")");
+    }
+
 } // Enc class CharAlphabet

Modified: db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/LoopingAlphabetReader.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/LoopingAlphabetReader.java?rev=750409&r1=750408&r2=750409&view=diff
==============================================================================
--- db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/LoopingAlphabetReader.java
(original)
+++ db/derby/code/branches/10.4/java/testing/org/apache/derbyTesting/functionTests/util/streams/LoopingAlphabetReader.java
Thu Mar  5 10:23:18 2009
@@ -112,7 +112,7 @@
         this.trailingBlanks = trailingBlanks;
         this.remainingNonBlanks = length - trailingBlanks;
         this.remainingBlanks = trailingBlanks;
-        this.alphabet = alphabet;
+        this.alphabet = alphabet.getClone();
         fillBuffer(alphabet.charCount());
     }
 



Mime
View raw message