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 2009/01/22 14:28:43 UTC

svn commit: r736636 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/services/io/ engine/org/apache/derby/iapi/types/ engine/org/apache/derby/impl/jdbc/ testing/org/apache/derbyTesting/unitTests/junit/

Author: kristwaa
Date: Thu Jan 22 05:28:42 2009
New Revision: 736636

URL: http://svn.apache.org/viewvc?rev=736636&view=rev
Log:
DERBY-3907 (partial): Save useful length information for Clobs in store.
Started using the new framework for handling stream headers for string data
values. The behavior regarding stream headers is kept unchanged, but the code
is now ready to deal with multiple stream header formats.
Short description:
 * EmbedResultSet & EmbedPreparesStatement
   Adjusted code to use the new interface method and pass in the correct
   class to the ReaderToUTF8Stream constructor.
   Note the special case of telling the DVD/generator if the database being
   accessed is in soft upgrade mode in EmbedResultSet.

 * ArrayInputStream
   The stream header is no longer read inside readDerbyUTF.

 * ReaderToUTF8Stream
   Adjusted code to use the new StreamHeaderGenerator interface, and made the
   stream count the number of characters encountered.
   If possible, the header is updated when the stream has been drained.

 * StringDataValue
   Added methods getStreamHeaderGenerator and setSoftUpgradeMode.

 * SQLChar
   Refactoring in preparation for handling multiple stream header formats.
   Pulled common code out into writeUTF. The header generator is now
   repsonsible for writing both the header bytes and an EOF marker if required.
   Made a second readExternal method, which is not reading the stream header
   format. This must now be done outside of this method and any length
   information is passed in as arguments.
   Implemented the new methods in StringDataValue.

 * SQLClob
   Adjusted a single call to ReaderToUTF8Stream.

 * UTF8UtilTest
   Adjusted code invoking the new ReaderToUTF8Stream constructor.

Patch file: derby-3907-7a2-use_new_framework.diff


Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/ArrayInputStream.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLChar.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/StringDataValue.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/UTF8UtilTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/ArrayInputStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/ArrayInputStream.java?rev=736636&r1=736635&r2=736636&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/ArrayInputStream.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/ArrayInputStream.java Thu Jan 22 05:28:42 2009
@@ -23,7 +23,6 @@
 
 import java.io.InputStream;
 import java.io.IOException;
-import java.io.ObjectInput;
 import java.io.EOFException;
 
 import org.apache.derby.iapi.services.sanity.SanityManager;
@@ -373,6 +372,9 @@
      * The routine returns the number of char's read into the returned
      * char[], note that this length may smaller than the actual length
      * of the char[] array.
+     * <p>
+     * The stream must be positioned on the first user byte when this method
+     * is invoked.
      *
 	 * @return The the number of valid char's in the returned char[].
      *
@@ -385,10 +387,11 @@
      *                      the filled in char[] - caller must allow that
      *                      the array may or may not be different from the
      *                      one passed in.
+     * @param utflen the byte length of the value, or {@code 0} if unknown
      *
 	 * @exception  StandardException  Standard exception policy.
      **/
-    public final int readDerbyUTF(char[][] rawData_array) 
+    public final int readDerbyUTF(char[][] rawData_array, int utflen)
         throws IOException
 	{
         // copy globals locally, to give compiler chance to optimize.
@@ -396,18 +399,6 @@
         int     end_pos = end;
  		int     pos     = position;
 
-        // get header length - stored as an unsigned short.
-
-		int utflen;
-        if (pos + 1 < end_pos) 
-        {
-            utflen = (((data[pos++] & 0xff) << 8) | (data[pos++] & 0xff));
-        }
-        else
-        {
-			throw new EOFException(); // end of file
-        }
-
         /**
          * 3 cases - can they all happen?
          *

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java?rev=736636&r1=736635&r2=736636&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java Thu Jan 22 05:28:42 2009
@@ -47,6 +47,8 @@
      */
 	private LimitReader reader;
 
+    /** 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.
@@ -59,19 +61,13 @@
     /** Tells if the stream content is/was larger than the buffer size. */
 	private boolean multipleBuffer;
     /**
-     * The stream header to use for this stream.
-     * <p>
-     * The holder object is immutable, and the header should not have to be
-     * changed, but we may replace it as an optimizataion. If the length of
-     * the stream is unknown at the start of the insertion and the whole stream
-     * content fits into the buffer, the header is updated with the length
-     * after the source stream has been drained. This means that even though
-     * the object is immutable and the reference final, another header may be
-     * written to the stream.
+     * The generator for the stream header to use for this stream.
      * @see #checkSufficientData()
      */
-    private final StreamHeaderHolder header;
-    
+    private final StreamHeaderGenerator hdrGen;
+    /** The length of the header. */
+    private int headerLength;
+
     /**
      * Number of characters to truncate from this stream.
      * The SQL standard allows for truncation of trailing spaces for CLOB,
@@ -92,6 +88,8 @@
     private final int valueLength; 
     /** The type name for the column data is inserted into. */
     private final String typeName;
+    /** The number of chars encoded. */
+    private int charCount;
     
     /**
      * Create a stream that will truncate trailing blanks if required/allowed.
@@ -107,18 +105,19 @@
      *      width if the expected stream length is unknown
      * @param numCharsToTruncate the number of trailing blanks to truncate
      * @param typeName type name of the column data is inserted into
+     * @param headerGenerator the stream header generator
      */
     public ReaderToUTF8Stream(Reader appReader,
                               int valueLength,
                               int numCharsToTruncate,
                               String typeName,
-                              StreamHeaderHolder headerHolder) {
+                              StreamHeaderGenerator headerGenerator) {
         this.reader = new LimitReader(appReader);
         reader.setLimit(valueLength);
         this.charsToTruncate = numCharsToTruncate;
         this.valueLength = valueLength;
         this.typeName = typeName;
-        this.header = headerHolder;
+        this.hdrGen = headerGenerator;
         if (SanityManager.DEBUG) {
             // Check the type name
             // The national types (i.e. NVARCHAR) are not used/supported.
@@ -145,13 +144,14 @@
      * @param maximumLength maximum allowed length in number of characters for
      *      the reader, typically the maximum field size
      * @param typeName type name of the column data is inserted into
+     * @param headerGenerator the stream header generator
      * @throws IllegalArgumentException if maximum length is negative
      */
     public ReaderToUTF8Stream(Reader appReader,
                               int maximumLength,
                               String typeName,
-                              StreamHeaderHolder headerHolder) {
-        this(appReader, -1 * maximumLength, 0, typeName, headerHolder);
+                              StreamHeaderGenerator headerGenerator) {
+        this(appReader, -1 * maximumLength, 0, typeName, headerGenerator);
         if (maximumLength < 0) {
             throw new IllegalArgumentException("Maximum length for a capped " +
                     "stream cannot be negative: " + maximumLength);
@@ -183,7 +183,7 @@
         
 		// first read
 		if (blen < 0)
-            fillBuffer(header.copyInto(buffer, 0));
+            fillBuffer(FIRST_READ);
 
 		while (boff == blen)
 		{
@@ -230,7 +230,7 @@
 
         // first read
 		if (blen < 0)
-            fillBuffer(header.copyInto(buffer, 0));
+            fillBuffer(FIRST_READ);
 
 		int readCount = 0;
 
@@ -287,6 +287,17 @@
      */
 	private void fillBuffer(int startingOffset) throws IOException
 	{
+        if (startingOffset == FIRST_READ) {
+            // Generate the header. Provide the char length only if the header
+            // encodes a char count and we actually know the char count.
+            if (hdrGen.expectsCharCount() && valueLength >= 0) {
+                headerLength = hdrGen.generateInto(buffer, 0, valueLength);
+            } else {
+                headerLength = hdrGen.generateInto(buffer, 0, -1);
+            }
+            // Make startingOffset point at the first byte after the header.
+            startingOffset = headerLength;
+        }
 		int off = boff = startingOffset;
 
 		if (off == 0)
@@ -301,6 +312,7 @@
 				eof = true;
 				break;
 			}
+            charCount++; // Increment the character count.
 
 			if ((c >= 0x0001) && (c <= 0x007F))
             {
@@ -388,37 +400,27 @@
 
         // can put the correct length into the stream.
         if (!multipleBuffer) {
-            StreamHeaderHolder tmpHeader = header;
-            if (header.expectsCharLength()) {
-                if (SanityManager.DEBUG) {
-                    SanityManager.THROWASSERT("Header update with character " +
-                            "length is not yet supported");
+            int newValueLen = -1;
+            if (hdrGen.expectsCharCount()) {
+                if (SanityManager.DEBUG && charCount == 0) {
+                    SanityManager.ASSERT(eof);
                 }
+                newValueLen = charCount;
             } else {
-                int utflen = blen - header.headerLength(); // Length in bytes
-                tmpHeader = header.updateLength(utflen, false);
-                // Update the header we have already written to our buffer,
-                // still at postition zero.
-                tmpHeader.copyInto(buffer, 0);
-                if (SanityManager.DEBUG) {
-                    // Check that we didn't overwrite any of the user data.
-                    SanityManager.ASSERT(
-                            header.headerLength() == tmpHeader.headerLength());
-                }
+                // Store the byte length of the user data (exclude the header).
+                newValueLen = blen - headerLength;
             }
-            // The if below is temporary, it won't be necessary when support
-            // for writing the new header has been added.
-            if (tmpHeader.writeEOF()) {
-                // Write the end-of-stream marker.
-                buffer[blen++] = (byte) 0xE0;
-                buffer[blen++] = (byte) 0x00;
-                buffer[blen++] = (byte) 0x00;
+            int newHeaderLength = hdrGen.generateInto(buffer, 0, newValueLen);
+            // Check that we didn't overwrite any of the user data.
+            if (newHeaderLength != headerLength) {
+                throw new IOException("Data corruption detected; user data " +
+                        "overwritten by header bytes");
             }
-        } else if (header.writeEOF()) {
-            // Write the end-of-stream marker.
-            buffer[blen++] = (byte) 0xE0;
-            buffer[blen++] = (byte) 0x00;
-            buffer[blen++] = (byte) 0x00;
+            // Write the end-of-stream marker (if required).
+            blen += hdrGen.writeEOF(buffer, blen, newValueLen);
+        } else {
+            // Write the end-of-stream marker (if required).
+            blen += hdrGen.writeEOF(buffer, blen, Math.max(valueLength, -1));
         }
     }
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLChar.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLChar.java?rev=736636&r1=736635&r2=736636&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLChar.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLChar.java Thu Jan 22 05:28:42 2009
@@ -145,15 +145,11 @@
     }
 
     /**
-     * Static stream header holder with the header used for a 10.4 (and earlier)
-     * stream with unknown byte length. This header will be used with 10.4 or
-     * earlier databases, and sometimes also in newer databases for the other
-     * string data types beside of Clob. The expected EOF marker is
-     * '0xE0 0x00 0x00'.
-     */
-    protected static final StreamHeaderHolder UNKNOWN_LEN_10_4_HEADER_HOLDER =
-            new StreamHeaderHolder(
-                    new byte[] {0x00, 0x00}, new byte[] {8, 0}, false, true);
+     * Stream header generator for CHAR, VARCHAR and LONG VARCHAR. Currently,
+     * only one header format is used for these data types.
+     */
+    protected static final StreamHeaderGenerator CHAR_HEADER_GENERATOR =
+            new CharStreamHeaderGenerator();
 
     /**************************************************************************
      * Fields of the class
@@ -765,6 +761,8 @@
     }
 
     /**
+        Writes a non-Clob data value to the modified UTF-8 format used by Derby.
+
         The maximum stored size is based upon the UTF format
         used to stored the String. The format consists of
         a two byte length field and a maximum number of three
@@ -853,18 +851,35 @@
             }
         }
 
-        boolean isLongUTF = false;
-        // for length than 64K, see format description above
-        if (utflen > 65535)
-        {
-            isLongUTF = true;
-            utflen = 0;
+        StreamHeaderGenerator header = getStreamHeaderGenerator();
+        if (SanityManager.DEBUG) {
+            SanityManager.ASSERT(!header.expectsCharCount());
         }
+        // Generate the header, write it to the destination stream, write the
+        // user data and finally write an EOF-marker is required.
+        header.generateInto(out, utflen);
+        writeUTF(out, strlen, isRaw);
+        header.writeEOF(out, utflen);
+    }
 
-        out.write((utflen >>> 8) & 0xFF);
-        out.write((utflen >>> 0) & 0xFF);
-        for (int i = 0 ; i < strlen ; i++)
-        {
+    /**
+     * Writes the user data value to a stream in the modified UTF-8 format.
+     *
+     * @param out destination stream
+     * @param strLen string length of the value
+     * @param isRaw {@code true} if the source is {@code rawData}, {@code false}
+     *      if the source is {@code value}
+     * @throws IOException if writing to the destination stream fails
+     */
+    private final void writeUTF(ObjectOutput out, int strLen,
+                                final boolean isRaw)
+            throws IOException {
+        // Copy the source reference into a local variable (optimization).
+        final char[] data = isRaw ? rawData : null;
+        final String lvalue = isRaw ? null : value;
+
+        // Iterate through the value and write it as modified UTF-8.
+        for (int i = 0 ; i < strLen ; i++) {
             int c = isRaw ? data[i] : lvalue.charAt(i);
             if ((c >= 0x0001) && (c <= 0x007F))
             {
@@ -882,15 +897,6 @@
                 out.write(0x80 | ((c >>  0) & 0x3F));
             }
         }
-
-        if (isLongUTF)
-        {
-            // write the following 3 bytes to terminate the string:
-            // (11100000, 00000000, 00000000)
-            out.write(0xE0);
-            out.write(0);
-            out.write(0);
-        }
     }
 
     /**
@@ -920,26 +926,52 @@
     public void readExternalFromArray(ArrayInputStream in) 
         throws IOException
     {
+        resetForMaterialization();
+        int utfLen = (((in.read() & 0xFF) << 8) | (in.read() & 0xFF));
+        if (rawData == null || rawData.length < utfLen) {
+            // This array may be as much as three times too big. This happens
+            // if the content is only 3-byte characters (i.e. CJK).
+            // TODO: Decide if a threshold should be introduced, where the
+            //       content is copied to a smaller array if the number of
+            //       unused array positions exceeds the threshold.
+            rawData = new char[utfLen];
+        }
         arg_passer[0]        = rawData;
 
-        rawLength = in.readDerbyUTF(arg_passer);
-
+        rawLength = in.readDerbyUTF(arg_passer, utfLen);
         rawData = arg_passer[0];
+    }
+    char[][] arg_passer = new char[1][];
 
-        // restoreToNull();
+    /**
+     * Resets state after materializing value from an array.
+     */
+    private void resetForMaterialization() {
         value  = null;
         stream = null;
-
         cKey = null;
     }
-    char[][] arg_passer = new char[1][];
 
     public void readExternal(ObjectInput in) throws IOException
     {
-        // if in.available() blocked at 0, use this default string size 
-
+        // Read the stored length in the stream header.
         int utflen = in.readUnsignedShort();
+        readExternal(in, utflen, 0);
+    }
 
+    /**
+     * Restores the data value from the source stream, materializing the value
+     * in memory.
+     *
+     * @param in the source stream
+     * @param utflen the byte length, or {@code 0} if unknown
+     * @param knownStrLen the char length, or {@code 0} if unknown
+     * @throws UTFDataFormatException if an encoding error is detected
+     * @throws IOException if reading the stream fails
+     */
+    protected void readExternal(ObjectInput in, int utflen,
+                                final int knownStrLen)
+            throws IOException {
         int requiredLength;
         // minimum amount that is reasonable to grow the array
         // when we know the array needs to growby at least one
@@ -974,13 +1006,13 @@
 
         // Set these to null to allow GC of the array if required.
         rawData = null;
-        restoreToNull();
-
+        resetForMaterialization();
         int count = 0;
         int strlen = 0;
 
 readingLoop:
-        while ( ((count < utflen) || (utflen == 0)))
+        while (((strlen < knownStrLen) || (knownStrLen == 0)) &&
+                ((count < utflen) || (utflen == 0)))
         {
             int c;
 
@@ -1106,8 +1138,8 @@
                                            ((char3 & 0x3F) << 0));
             }
             else {
-
-                throw new UTFDataFormatException();
+                throw new UTFDataFormatException(
+                        "Invalid code point: " + Integer.toHexString(c));
             }
 
             str[strlen++] = actualChar;
@@ -1116,8 +1148,6 @@
 
         rawData = str;
         rawLength = strlen;
-                        
-        cKey = null;
     }
 
     /**
@@ -2879,24 +2909,22 @@
     }
 
     /**
-     * Generates the stream header for a stream with the given character length.
+     * Returns the default stream header generator for the string data types.
+     *
+     * @return A stream header generator.
+     */
+    public StreamHeaderGenerator getStreamHeaderGenerator() {
+        return CHAR_HEADER_GENERATOR;
+    }
+
+    /**
+     * Sets the mode for the database being accessed.
      *
-     * @param charLength the character length of the stream, or {@code -1} if
-     *      unknown. If unknown, it is expected that a specifiec end-of-stream
-     *      byte sequence is appended to the stream.
-     * @return A holder object with the stream header. A holder object is used
-     *      because more information than the raw header itself is required,
-     *      for instance whether the stream should be ended with a Derby-
-     *      specific end-of-stream marker
-     */
-    public StreamHeaderHolder generateStreamHeader(long charLength) {
-        // Support for old (pre 10.5) deprecated format, which expects the
-        // header to contain the number of bytes in the value.
-        // We don't know that (due to the varying number of bytes per char), so
-        // say we don't know and instruct that the stream must be ended with a
-        // Derby-specific end-of-stream marker.
-        // Note that there are other code paths were the byte length is known
-        // and can be written to the stream.
-        return UNKNOWN_LEN_10_4_HEADER_HOLDER;
+     * @param inSoftUpgradeMode {@code true} if the database is being accessed
+     *      in soft upgrade mode, {@code false} if not, and {@code null} if
+     *      unknown
+     */
+    public void setSoftUpgradeMode(Boolean inSoftUpgradeMode) {
+        // Ignore this for CHAR, VARCHAR and LONG VARCHAR.
     }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java?rev=736636&r1=736635&r2=736636&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SQLClob.java Thu Jan 22 05:28:42 2009
@@ -451,7 +451,7 @@
 
             ReaderToUTF8Stream utfIn = new ReaderToUTF8Stream(
                     vc.getCharacterStream(), (int) vcl, 0, TypeId.CLOB_NAME,
-                    generateStreamHeader(vcl));
+                    getStreamHeaderGenerator());
             setValue(utfIn, (int) vcl);
         } catch (SQLException e) {
             throw dataTypeConversion("DAN-438-tmp");

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/StringDataValue.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/StringDataValue.java?rev=736636&r1=736635&r2=736636&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/StringDataValue.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/StringDataValue.java Thu Jan 22 05:28:42 2009
@@ -197,17 +197,28 @@
 	public StringDataValue getValue(RuleBasedCollator collatorForComparison);
 
     /**
-     * Generates the stream header for a stream with the given character length.
+     * Returns the stream header generator for the string data value.
+     * <p>
+     * The generator writes the correct header into the destination buffer or
+     * stream and also keeps track of whether appending an end-of-stream marker
+     * is required or not.
+     * <p>
+     * Note that the generator may fail to generate a header if there is no
+     * context at the time the header is asked for, and the mode hasn't been
+     * set explicitly.
+     * @see #setSoftUpgradeMode
+     */
+    public StreamHeaderGenerator getStreamHeaderGenerator();
+
+    /**
+     * Tells the data value descriptor whether the database is being accessed
+     * in soft upgrade mode or not.
      *
-     * @param charLength the character length of the stream, or {@code -1} if
-     *      unknown. If unknown, it is expected that an end-of-stream byte
-     *      sequence is appended to the stream.
-     * @return A holder object with the stream header. A holder object is used
-     *      because more information than the raw header itself is required,
-     *      for instance whether the stream should be ended with a Derby-
-     *      specific end-of-stream marker.
+     * @param inSoftUpgradeMode {@code true} if the database is being accessed
+     *      in soft upgrade mode, {@code false} if not, and {@code null} if
+     *      unknown
      */
-    public StreamHeaderHolder generateStreamHeader(long charLength);
+    public void setSoftUpgradeMode(Boolean inSoftUpgradeMode);
 
     /**
      * Returns a descriptor for the input stream for this data value.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java?rev=736636&r1=736635&r2=736636&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java Thu Jan 22 05:28:42 2009
@@ -782,13 +782,13 @@
                 // Create a stream with truncation.
                 utfIn = new ReaderToUTF8Stream(reader, usableLength,
                         truncationLength, getParameterSQLType(parameterIndex),
-                        dvd.generateStreamHeader(length));
+                        dvd.getStreamHeaderGenerator());
             } else {
                 // Create a stream without exactness checks,
                 // but with a maximum limit.
                 utfIn = new ReaderToUTF8Stream(reader, colWidth,
-                                getParameterSQLType(parameterIndex),
-                                dvd.generateStreamHeader(-1));
+                        getParameterSQLType(parameterIndex),
+                        dvd.getStreamHeaderGenerator());
             }
 
             // JDBC is one-based, DBMS is zero-based.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java?rev=736636&r1=736635&r2=736636&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java Thu Jan 22 05:28:42 2009
@@ -77,6 +77,7 @@
 import java.util.Calendar;
 
 import org.apache.derby.iapi.jdbc.CharacterStreamDescriptor;
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;
 import org.apache.derby.iapi.types.StringDataValue;
 
 /**
@@ -2926,6 +2927,13 @@
             
             final StringDataValue dvd = (StringDataValue)
                     getDVDforColumnToBeUpdated(columnIndex, updateMethodName);
+            // In the case of updatable result sets, we cannot guarantee that a
+            // context is pushed when the header needs to be generated. To fix
+            // this, tell the DVD/generator whether we are running in soft
+            // upgrade mode or not.
+            dvd.setSoftUpgradeMode(Boolean.valueOf(
+                    !getEmbedConnection().getDatabase().getDataDictionary().
+                    checkVersion(DataDictionary.DD_VERSION_CURRENT, null)));
             ReaderToUTF8Stream utfIn;
             int usableLength = -1;
             if (!lengthLess) {
@@ -2976,12 +2984,12 @@
 
                 utfIn = new ReaderToUTF8Stream(reader, usableLength,
                         truncationLength, getColumnSQLType(columnIndex),
-                        dvd.generateStreamHeader(length));
+                        dvd.getStreamHeaderGenerator());
             } else {
                 int colWidth = getMaxColumnWidth(columnIndex);
-                utfIn = new ReaderToUTF8Stream(
-                            reader, colWidth, getColumnSQLType(columnIndex),
-                            dvd.generateStreamHeader(-1));
+                utfIn = new ReaderToUTF8Stream(reader, colWidth,
+                                               getColumnSQLType(columnIndex),
+                                               dvd.getStreamHeaderGenerator());
             }
 
             // NOTE: The length argument to setValue is not used. If that

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/UTF8UtilTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/UTF8UtilTest.java?rev=736636&r1=736635&r2=736636&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/UTF8UtilTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/junit/UTF8UtilTest.java Thu Jan 22 05:28:42 2009
@@ -32,8 +32,9 @@
 import java.io.InputStream;
 import java.io.UTFDataFormatException;
 
+import org.apache.derby.iapi.types.CharStreamHeaderGenerator;
+import org.apache.derby.iapi.types.ClobStreamHeaderGenerator;
 import org.apache.derby.iapi.types.ReaderToUTF8Stream;
-import org.apache.derby.iapi.types.StreamHeaderHolder;
 import org.apache.derby.iapi.util.UTF8Util;
 
 import org.apache.derbyTesting.functionTests.util.streams.CharAlphabet;
@@ -63,10 +64,11 @@
     /** Type name passed to {@code ReaderToUTF8Stream}. */
     private static final String TYPENAME = "VARCHAR";
 
-    /** Default header for stream with unknown length. */
-    private static final StreamHeaderHolder HDR = new StreamHeaderHolder(
-            new byte[] {0x00, 0x00}, new byte[] {8, 0}, false, true);
-    private static final int HEADER_LENGTH = HDR.headerLength();
+    /**
+     * Hardcoded header length. This is why the Clob stream header generator
+     * is invoked with {@code true} in the constructor.
+     */
+    private static final int HEADER_LENGTH = 2;
 
     /**
      * Creates a test of the specified name.
@@ -87,7 +89,8 @@
         InputStream ascii = new LoopingAlphabetStream(length);
         InputStream modUTF8 = new ReaderToUTF8Stream(
                                     new LoopingAlphabetReader(length),
-                                    length, 0, TYPENAME, HDR);
+                                    length, 0, TYPENAME,
+                                    new CharStreamHeaderGenerator());
         modUTF8.skip(HEADER_LENGTH); // Skip encoded length added by ReaderToUTF8Stream.
         assertEquals(ascii, modUTF8);
     }
@@ -107,7 +110,7 @@
         final int charLength = 5;
         InputStream in = new ReaderToUTF8Stream(
                 new LoopingAlphabetReader(charLength, CharAlphabet.cjkSubset()),
-                charLength, 0, TYPENAME, HDR);
+                charLength, 0, TYPENAME, new CharStreamHeaderGenerator());
         in.skip(HEADER_LENGTH); // Skip encoded length added by ReaderToUTF8Stream.
         assertEquals(charLength, UTF8Util.skipUntilEOF(in));
     }
@@ -123,7 +126,7 @@
         final int charLength = 127019;
         InputStream in = new ReaderToUTF8Stream(
                 new LoopingAlphabetReader(charLength, CharAlphabet.cjkSubset()),
-                charLength, 0, TYPENAME, HDR);
+                charLength, 0, TYPENAME, new ClobStreamHeaderGenerator(true));
         in.skip(HEADER_LENGTH); // Skip encoded length added by ReaderToUTF8Stream.
         assertEquals(charLength, UTF8Util.skipUntilEOF(in));
     }
@@ -139,7 +142,7 @@
         final int charLength = 161019;
         InputStream in = new ReaderToUTF8Stream(
                 new LoopingAlphabetReader(charLength, CharAlphabet.cjkSubset()),
-                charLength, 0, TYPENAME, HDR);
+                charLength, 0, TYPENAME, new CharStreamHeaderGenerator());
         in.skip(HEADER_LENGTH); // Skip encoded length added by ReaderToUTF8Stream.
         // Returns count in bytes, we are using CJK chars so multiply length
         // with 3 to get expected number of bytes.
@@ -157,7 +160,7 @@
         final int charLength = 161019;
         InputStream in = new ReaderToUTF8Stream(
                 new LoopingAlphabetReader(charLength, CharAlphabet.cjkSubset()),
-                charLength, 0, TYPENAME, HDR);
+                charLength, 0, TYPENAME, new ClobStreamHeaderGenerator(true));
         in.skip(HEADER_LENGTH); // Skip encoded length added by ReaderToUTF8Stream.
         try {
             UTF8Util.skipFully(in, charLength + 100);
@@ -178,7 +181,7 @@
         final int charLength = 10;
         InputStream in = new ReaderToUTF8Stream(
                 new LoopingAlphabetReader(charLength, CharAlphabet.cjkSubset()),
-                charLength, 0, TYPENAME, HDR);
+                charLength, 0, TYPENAME, new CharStreamHeaderGenerator());
         in.skip(HEADER_LENGTH); // Skip encoded length added by ReaderToUTF8Stream.
         in.skip(1L); // Skip one more byte to trigger a UTF error.
         try {
@@ -197,7 +200,7 @@
         final int charLength = 161019;
         InputStream in = new ReaderToUTF8Stream(
                 new LoopingAlphabetReader(charLength, CharAlphabet.tamil()),
-                charLength, 0, TYPENAME, HDR);
+                charLength, 0, TYPENAME, new CharStreamHeaderGenerator());
         // Skip encoded length added by ReaderToUTF8Stream.
         in.skip(HEADER_LENGTH);
         int firstSkip = 10078;