db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From krist...@apache.org
Subject svn commit: r762389 - in /db/derby/code/branches/10.5/java: engine/org/apache/derby/iapi/types/ engine/org/apache/derby/loc/ shared/org/apache/derby/shared/common/reference/ testing/org/apache/derbyTesting/unitTests/junit/
Date Mon, 06 Apr 2009 15:32:45 GMT
Author: kristwaa
Date: Mon Apr  6 15:32:45 2009
New Revision: 762389

URL: http://svn.apache.org/viewvc?rev=762389&view=rev
Log:
DERBY-4122: java/testing/org/apache/derbyTesting/unitTests/junit/ReaderToUTF8StreamTest.java.
Merged revision 762384 (patch 4c) from trunk to 10.5.

Added:
    db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/unitTests/junit/ReaderToUTF8StreamTest.java
      - copied unchanged from r762384, db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/ReaderToUTF8StreamTest.java
Modified:
    db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
    db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/SQLClob.java
    db/derby/code/branches/10.5/java/engine/org/apache/derby/loc/messages.xml
    db/derby/code/branches/10.5/java/shared/org/apache/derby/shared/common/reference/MessageId.java
    db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/unitTests/junit/_Suite.java

Modified: db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java?rev=762389&r1=762388&r2=762389&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
(original)
+++ db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
Mon Apr  6 15:32:45 2009
@@ -30,6 +30,7 @@
 import org.apache.derby.iapi.services.io.DerbyIOException;
 import org.apache.derby.iapi.services.io.LimitReader;
 import org.apache.derby.iapi.services.sanity.SanityManager;
+import org.apache.derby.shared.common.reference.MessageId;
 
 /**
  * Converts the characters served by a {@code java.io.Reader} to a stream
@@ -39,6 +40,7 @@
  * Length validation is performed. If required and allowed by the target column
  * type, truncation of blanks will also be performed.
  */
+//@NotThreadSafe
 public final class ReaderToUTF8Stream
 	extends InputStream
 {
@@ -50,13 +52,22 @@
     /** Constant indicating the first iteration of {@code fillBuffer}. */
     private final static int FIRST_READ = Integer.MIN_VALUE;
     /**
-     * Size of buffer to hold the data read from stream and converted to the
-     * modified UTF-8 format.
+     * Constant indicating that no mark is set in the stream, or that the read
+     * ahead limit of the mark has been exceeded.
      */
-    private final static int BUFSIZE = 32768;
-    private byte[] buffer = new byte[BUFSIZE];
+    private final static int MARK_UNSET_OR_EXCEEDED = -1;
+    /**
+     * Buffer to hold the data read from stream and converted to the modified
+     * UTF-8 format. The initial size is 32 KB, but it may grow if the
+     * {@linkplain #mark(int)} is invoked.
+     */
+    private byte[] buffer = new byte[32*1024];
 	private int boff;
     private int blen = -1;
+    /** Stream mark, set through {@linkplain #mark(int)}. */
+    private int mark = MARK_UNSET_OR_EXCEEDED;
+    /** Read ahead limit for mark, set through {@linkplain #mark(int)}. */
+    private int readAheadLimit;
 	private boolean eof;
     /** Tells if the stream content is/was larger than the buffer size. */
 	private boolean multipleBuffer;
@@ -298,11 +309,42 @@
             // Make startingOffset point at the first byte after the header.
             startingOffset = headerLength;
         }
-		int off = boff = startingOffset;
+		int off = startingOffset;
+        // In the case of a mark, the offset may be adjusted.
+        // Do not change boff in the encoding loop. Before the encoding loop
+        // starts, it shall point at the next byte the stream will deliver on
+        // the next iteration of read or skip.
+        boff = 0;
 
 		if (off == 0)
 			multipleBuffer = true;
 
+        // If we have a mark set, see if we have to expand the buffer, or if we
+        // are going to read past the read ahead limit and can invalidate the
+        // mark and discard the data currently in the buffer.
+        if (mark >= 0) {
+            // Add 6 bytes reserved for one 3 byte character encoding and the
+            // 3 byte Derby EOF marker (see encoding loop further down).
+            int spaceRequired = readAheadLimit + 6;
+            if (mark + spaceRequired > buffer.length) {
+                if (blen != -1) {
+                    // Calculate the new offset, as we may have to shift bytes
+                    // we have already delivered to the left.
+                    boff = off = blen - mark;
+                }
+                byte[] oldBuf = buffer;
+                if (spaceRequired > buffer.length) {
+                    // We have to allocate a bigger buffer to save the bytes.
+                    buffer = new byte[spaceRequired];
+                }
+                System.arraycopy(oldBuf, mark, buffer, 0, off);
+                mark = 0;
+            } else if (blen != -1) {
+                // Invalidate the mark.
+                mark = MARK_UNSET_OR_EXCEEDED;
+            }
+        }
+
 		// 6! need to leave room for a three byte UTF8 encoding
 		// and 3 bytes for our special end of file marker.
 		for (; off <= buffer.length - 6; )
@@ -332,8 +374,6 @@
 		}
 
 		blen = off;
-		boff = 0;
-
 		if (eof)
 			checkSufficientData();
 	}
@@ -487,6 +527,63 @@
        // from the reader object 
        // reader.getLimit() returns the remaining bytes available
        // on this stream
-       return (BUFSIZE > remainingBytes ? remainingBytes : BUFSIZE);
+       return (buffer.length > remainingBytes ? remainingBytes : buffer.length);
+    }
+
+    /**
+     * Marks the current position in the stream.
+     * <p>
+     * Note that this stream is not marked at position zero by default (i.e.
+     * in the constructor).
+     *
+     * @param readAheadLimit the maximum limit of bytes that can be read before
+     *      the mark position becomes invalid
+     */
+    public void mark(int readAheadLimit) {
+        if (readAheadLimit > 0) {
+            this.readAheadLimit = readAheadLimit;
+            mark = boff;
+        } else {
+            this.readAheadLimit = mark = MARK_UNSET_OR_EXCEEDED;
+        }
+    }
+
+    /**
+     * Repositions this stream to the position at the time the mark method was
+     * last called on this input stream.
+     *
+     * @throws EOFException if the stream has been closed
+     * @throws IOException if no mark has been set, or the read ahead limit of
+     *      the mark has been exceeded
+     */
+    public void reset()
+            throws IOException {
+        // Throw execption if the stream has been closed implicitly or
+        // explicitly.
+        if (buffer == null) {
+            throw new EOFException(MessageService.getTextMessage
+                    (SQLState.STREAM_EOF));
+        }
+        // Throw exception if the mark hasn't been set, or if we had to refill
+        // the internal buffer after we had read past the read ahead limit.
+        if (mark == MARK_UNSET_OR_EXCEEDED) {
+            throw new IOException(MessageService.getTextMessage(
+                    MessageId.STREAM_MARK_UNSET_OR_EXCEEDED));
+        }
+        // Reset successful, adjust state.
+        boff = mark;
+        readAheadLimit = mark = MARK_UNSET_OR_EXCEEDED;
+    }
+
+    /**
+     * Tests if this stream supports mark/reset.
+     * <p>
+     * The {@code markSupported} method of {@code ByteArrayInputStream} always
+     * returns {@code true}.
+     *
+     * @return {@code true}, mark/reset is always supported.
+     */
+    public boolean markSupported() {
+        return true;
     }
 }

Modified: db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/SQLClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/SQLClob.java?rev=762389&r1=762388&r2=762389&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/SQLClob.java (original)
+++ db/derby/code/branches/10.5/java/engine/org/apache/derby/iapi/types/SQLClob.java Mon Apr
 6 15:32:45 2009
@@ -32,6 +32,8 @@
 import org.apache.derby.iapi.services.sanity.SanityManager;
 import org.apache.derby.iapi.util.UTF8Util;
 
+import org.apache.derby.shared.common.reference.SQLState;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectInput;
@@ -331,6 +333,11 @@
                 // Assume new header format, adjust later if necessary.
                 byte[] header = new byte[MAX_STREAM_HEADER_LENGTH];
                 int read = stream.read(header);
+                // Expect at least two header bytes.
+                if (SanityManager.DEBUG) {
+                    SanityManager.ASSERT(read > 1,
+                            "Too few header bytes: " + read);
+                }
                 HeaderInfo hdrInfo = investigateHeader(header, read);
                 if (read > hdrInfo.headerLength()) {
                     // We have read too much. Reset the stream.
@@ -346,6 +353,21 @@
                     byteLength(hdrInfo.byteLength()).
                     charLength(hdrInfo.charLength()).build();
             } catch (IOException ioe) {
+                // Check here to see if the root cause is a container closed
+                // exception. If so, this most likely means that the Clob was
+                // accessed after a commit or rollback on the connection.
+                Throwable rootCause = ioe;
+                while (rootCause.getCause() != null) {
+                    rootCause = rootCause.getCause();
+                }
+                if (rootCause instanceof StandardException) {
+                    StandardException se = (StandardException)rootCause;
+                    if (se.getMessageId().equals(
+                            SQLState.DATA_CONTAINER_CLOSED)) {
+                        throw StandardException.newException(
+                                SQLState.BLOB_ACCESSED_AFTER_COMMIT, ioe);
+                    }
+                }
                 throwStreamingIOException(ioe);
             }
         }
@@ -512,11 +534,19 @@
             long vcl = vc.length();
             if (vcl < 0L || vcl > Integer.MAX_VALUE)
                 throw this.outOfRange();
-
-            ReaderToUTF8Stream utfIn = new ReaderToUTF8Stream(
-                    vc.getCharacterStream(), (int) vcl, 0, TypeId.CLOB_NAME,
-                    getStreamHeaderGenerator());
-            setValue(utfIn, (int) vcl);
+            // For small values, just materialize the value.
+            // NOTE: Using streams for the empty string ("") isn't supported
+            // down this code path when in soft upgrade mode, because the code
+            // reading the header bytes ends up reading zero bytes (i.e., it
+            // doesn't get the header / EOF marker).
+            if (vcl < 32*1024) {
+                setValue(vc.getSubString(1, (int)vcl));
+            } else {
+                ReaderToUTF8Stream utfIn = new ReaderToUTF8Stream(
+                        vc.getCharacterStream(), (int) vcl, 0, TypeId.CLOB_NAME,
+                        getStreamHeaderGenerator());
+                setValue(utfIn, (int) vcl);
+            }
         } catch (SQLException e) {
             throw dataTypeConversion("DAN-438-tmp");
        }
@@ -658,17 +688,42 @@
             // Make sure the stream is correctly positioned.
             rewindStream(hdrLen);
         } else {
+            final boolean markSet = stream.markSupported();
+            if (markSet) {
+                stream.mark(MAX_STREAM_HEADER_LENGTH);
+            }
             byte[] header = new byte[MAX_STREAM_HEADER_LENGTH];
             int read = in.read(header);
+            // Expect at least two header bytes.
+            if (SanityManager.DEBUG) {
+                SanityManager.ASSERT(read > 1, "Too few header bytes: " + read);
+            }
             hdrInfo = investigateHeader(header, read);
             if (read > hdrInfo.headerLength()) {
                 // We read too much data, reset and position on the first byte
                 // of the user data.
-                rewindStream(hdrInfo.headerLength());
+                // First see if we set a mark on the stream and can reset it.
+                // If not, try using the Resetable interface.
+                if (markSet) {
+                    // Stream is not a store Resetable one, use mark/reset
+                    // functionality instead.
+                    stream.reset();
+                    InputStreamUtil.skipFully(stream, hdrInfo.headerLength());
+                } else if (stream instanceof Resetable) {
+                    // We have a store stream.
+                    rewindStream(hdrInfo.headerLength());
+                }
             }
         }
         // The data will be materialized in memory, in a char array.
-        super.readExternal(in, hdrInfo.byteLength(), hdrInfo.charLength());
+        // Subtract the header length from the byte length if there is a byte
+        // encoded in the header, otherwise the decode routine will try to read
+        // too many bytes.
+        int byteLength = 0; // zero is interpreted as unknown / unset
+        if (hdrInfo.byteLength() != 0) {
+            byteLength = hdrInfo.byteLength() - hdrInfo.headerLength();
+        }
+        super.readExternal(in, byteLength, hdrInfo.charLength());
     }
 
     /**
@@ -686,6 +741,10 @@
         int prevPos = in.getPosition();
         byte[] header = new byte[MAX_STREAM_HEADER_LENGTH];
         int read = in.read(header);
+        // Expect at least two header bytes.
+        if (SanityManager.DEBUG) {
+            SanityManager.ASSERT(read > 1, "Too few header bytes: " + read);
+        }
         HeaderInfo hdrInfo = investigateHeader(header, read);
         if (read > hdrInfo.headerLength()) {
             // Reset stream. This path will only be taken for Clobs stored

Modified: db/derby/code/branches/10.5/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/engine/org/apache/derby/loc/messages.xml?rev=762389&r1=762388&r2=762389&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/branches/10.5/java/engine/org/apache/derby/loc/messages.xml Mon Apr  6 15:32:45
2009
@@ -7401,6 +7401,11 @@
                 <arg>errorMessage</arg>
             </msg>
 
+            <msg>
+                <name>I027</name>
+                <text>No mark set, or mark read ahead limit exceeded.</text>
+            </msg>
+
         </family>
 
 

Modified: db/derby/code/branches/10.5/java/shared/org/apache/derby/shared/common/reference/MessageId.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/shared/org/apache/derby/shared/common/reference/MessageId.java?rev=762389&r1=762388&r2=762389&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/shared/org/apache/derby/shared/common/reference/MessageId.java
(original)
+++ db/derby/code/branches/10.5/java/shared/org/apache/derby/shared/common/reference/MessageId.java
Mon Apr  6 15:32:45 2009
@@ -184,6 +184,11 @@
 	String CORE_DATABASE_NOT_AVAILABLE	= "I024"; // Database not available
 	String CORE_DRIVER_NOT_AVAILABLE	= "I025"; // JDBC Driver not available
 	String JDBC_DRIVER_REGISTER_ERROR 	= "I026"; // Error while registering driver
+    /**
+     * At the time InputStream.reset was invoked, either no mark was set or the
+     * read ahead limit of the mark was exceeded.
+     */
+    String STREAM_MARK_UNSET_OR_EXCEEDED                    = "I027";
 
     /*
      * Monitor

Modified: db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/unitTests/junit/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/unitTests/junit/_Suite.java?rev=762389&r1=762388&r2=762389&view=diff
==============================================================================
--- db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/unitTests/junit/_Suite.java
(original)
+++ db/derby/code/branches/10.5/java/testing/org/apache/derbyTesting/unitTests/junit/_Suite.java
Mon Apr  6 15:32:45 2009
@@ -57,6 +57,7 @@
         suite.addTest(BlockedByteArrayTest.suite());
         suite.addTest(PathUtilTest.suite());
         suite.addTest(VirtualFileTest.suite());
+        suite.addTest(ReaderToUTF8StreamTest.suite());
 
         return suite;
     }



Mime
View raw message