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 ka...@apache.org on 2006/08/26 13:39:22 UTC

svn commit: r437146 [1/2] - in /db/derby/code/branches/10.2/java: engine/org/apache/derby/iapi/services/io/ engine/org/apache/derby/iapi/types/ engine/org/apache/derby/impl/jdbc/ testing/org/apache/derbyTesting/functionTests/master/ testing/org/apache/...

Author: kahatlen
Date: Sat Aug 26 04:39:20 2006
New Revision: 437146

URL: http://svn.apache.org/viewvc?rev=437146&view=rev
Log:
DERBY-1473: Add cut-off and truncation logic to streaming classes in
the embedded driver

Merged fix from trunk.

Added:
    db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java   (with props)
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/streams/
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/streams/ByteAlphabet.java   (with props)
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/streams/CharAlphabet.java   (with props)
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/streams/LoopingAlphabetReader.java   (with props)
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/streams/LoopingAlphabetStream.java   (with props)
Modified:
    db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.java
    db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
    db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SQLClob.java
    db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
    db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/characterStreams.out
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/build.xml

Added: db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java?rev=437146&view=auto
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java (added)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java Sat Aug 26 04:39:20 2006
@@ -0,0 +1,56 @@
+/*
+
+   Derby - Class org.apache.derby.iapi.service.io.DerbyIOException
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+
+package org.apache.derby.iapi.services.io;
+
+import java.io.IOException;
+
+/**
+ * A subclass of <code>IOException</code> that carries a SQL state.
+ *
+ * The original reason for adding it was to separate between
+ * <code>IOException</code>s generated by the application stream and the ones
+ * generated by the Derby wrapper streams, see for instance
+ * <code>RawToBinaryFormatStream</code>. Without this distinction, the user
+ * would not be able to easily write <code>catch</code>-blocks to handle
+ * specific errors happening when reading streams.
+ */
+public final class DerbyIOException
+    extends IOException {
+
+    /** A Derby SQLState. */
+    private final String sqlState;
+
+    /**
+     * Create a new Derby IO exception.
+     *
+     * @param msg a string describing the error
+     * @param sqlState a Derby SQLState describing the error
+     */
+    public DerbyIOException(String msg, String sqlState) {
+        super(msg);
+        this.sqlState = sqlState;
+    }
+
+    public String getSQLState() {
+        return sqlState;
+    }
+} // End class DerbyIOException

Propchange: db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.java?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.java (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.java Sat Aug 26 04:39:20 2006
@@ -25,6 +25,7 @@
 import java.io.IOException;
 import java.io.EOFException;
 
+import org.apache.derby.iapi.services.io.DerbyIOException;
 import org.apache.derby.iapi.services.io.LimitInputStream;
 import org.apache.derby.iapi.services.i18n.MessageService;
 import org.apache.derby.iapi.reference.SQLState;
@@ -64,44 +65,103 @@
     // and eof reached
     private boolean eof = false;
 
-	/**
-		@param	in Application's raw binary stream passed into JDBC layer
-		@param	length - length of the stream, if known, otherwise -1.
-	*/
-	public RawToBinaryFormatStream(InputStream in, int length) {
-		super(in);
+    /**
+     * The length of the stream.
+     * Unknown if less than 0.
+     */
+    private final int length;
+    /**
+     * The maximum allowed length for the stream.
+     * No limit if less than 0.
+     */
+    private final int maximumLength;
+    /**
+     * The type of the column the stream is inserted into.
+     * Used for length less streams, <code>null</code> if not in use.
+     */
+    private final String typeName;
 
-		if (length >= 0) {
-			setLimit(length);
-            
-            if (length <= 31)
-            {
-                encodedLength = new byte[1];               
-                encodedLength[0] = (byte) (0x80 | (length & 0xff));
-            }
-            else if (length <= 0xFFFF)
-            {
-                encodedLength = new byte[3];
-                encodedLength[0] = (byte) 0xA0;
-                encodedLength[1] = (byte)(length >> 8);
-                encodedLength[2] = (byte)(length);    
-            }
-            else
-            {
-                encodedLength = new byte[5];
-                encodedLength[0] = (byte) 0xC0;
-                encodedLength[1] = (byte)(length >> 24);
-                encodedLength[2] = (byte)(length >> 16);
-                encodedLength[3] = (byte)(length >> 8);
-                encodedLength[4] = (byte)(length);
-            }
-		}
+    /**
+     * Create a binary on-disk stream from the given <code>InputStream</code>.
+     *
+     * The on-disk stream prepends a length encoding, and validates that the
+     * actual length of the stream matches the specified length (as according
+     * to JDBC 3.0).
+     *
+     * @param in application's raw binary stream passed into JDBC layer
+     * @param length length of the stream
+     * @throws IllegalArgumentException if <code>length</code> is negative.
+     *      This exception should never be exposed to the user, and seeing it
+     *      means a programming error exists in the code.
+     */
+    public RawToBinaryFormatStream(InputStream in, int length) {
+        super(in);
+        if (length < 0) {
+            throw new IllegalArgumentException(
+                    "Stream length cannot be negative: " + length);
+        }
+        this.length = length;
+        this.maximumLength = -1;
+        this.typeName = null;
+
+        setLimit(length);
+        
+        if (length <= 31)
+        {
+            encodedLength = new byte[1];               
+            encodedLength[0] = (byte) (0x80 | (length & 0xff));
+        }
+        else if (length <= 0xFFFF)
+        {
+            encodedLength = new byte[3];
+            encodedLength[0] = (byte) 0xA0;
+            encodedLength[1] = (byte)(length >> 8);
+            encodedLength[2] = (byte)(length);    
+        }
         else
         {
-            // unknown length, four zero bytes
-            encodedLength = new byte[4];
+            encodedLength = new byte[5];
+            encodedLength[0] = (byte) 0xC0;
+            encodedLength[1] = (byte)(length >> 24);
+            encodedLength[2] = (byte)(length >> 16);
+            encodedLength[3] = (byte)(length >> 8);
+            encodedLength[4] = (byte)(length);
         }
-	}
+    }
+
+    /**
+     * Create a binary on-disk stream from the given <code>InputStream</code>
+     * of unknown length.
+     *
+     * A limit is placed on the maximum length of the stream.
+     *
+     * @param in the application stream
+     * @param maximumLength maximum length of the column data is inserted into
+     * @param typeName type name for the column data is inserted into
+     * @throws IllegalArgumentException if maximum length is negative, or type
+     *      name is <code>null<code>. This exception should never be exposed
+     *      to the user, and seeing it means a programming error exists in the
+     *      code. Although a missing type name is not critical, an exception is
+     *      is thrown to signal the intended use of this constructor.
+     */
+    public RawToBinaryFormatStream(InputStream in,
+                                   int maximumLength,
+                                   String typeName) {
+        super(in);
+        if (maximumLength < 0) {
+            throw new IllegalArgumentException("Maximum length for a capped " +
+                    "stream cannot be negative: " + maximumLength);
+        }
+        if (typeName == null) {
+            throw new IllegalArgumentException("Type name cannot be null");
+        }
+        this.length = -1;
+        this.maximumLength = maximumLength;
+        this.typeName = typeName;
+        // Unknown length, four zero bytes.
+        encodedLength = new byte[4];
+        setLimit(maximumLength);
+    }
 
 	/**
 		Read from the wrapped stream prepending the intial bytes if needed.
@@ -143,8 +203,12 @@
 
 		int remainingBytes = clearLimit();
 
-		if (remainingBytes > 0)
-			throw new IOException(MessageService.getTextMessage(SQLState.SET_STREAM_INEXACT_LENGTH_DATA));
+        if (length > -1 && remainingBytes > 0) {
+            throw new DerbyIOException(
+                        MessageService.getTextMessage(
+                                    SQLState.SET_STREAM_INEXACT_LENGTH_DATA),
+                        SQLState.SET_STREAM_INEXACT_LENGTH_DATA);
+        }
 
 		// if we had a limit try reading one more byte.
 		// JDBC 3.0 states the stream muct have the correct number of characters in it.
@@ -157,8 +221,25 @@
 			catch (IOException ioe) {
 				c = -1;
 			}
-			if (c != -1)
-				throw new IOException(MessageService.getTextMessage(SQLState.SET_STREAM_INEXACT_LENGTH_DATA));
+			if (c != -1) {
+                if (length > -1) {
+                    // Stream is not capped, and should have matched the
+                    // specified length.
+                    throw new DerbyIOException(
+                                MessageService.getTextMessage(
+                                    SQLState.SET_STREAM_INEXACT_LENGTH_DATA),
+                                SQLState.SET_STREAM_INEXACT_LENGTH_DATA);
+                } else {
+                    // Stream is capped, and has exceeded the maximum length.
+                    throw new DerbyIOException(
+                            MessageService.getTextMessage(
+                                    SQLState.LANG_STRING_TRUNCATION,
+                                    typeName,
+                                    "XXXX",
+                                    String.valueOf(maximumLength)),
+                            SQLState.LANG_STRING_TRUNCATION);
+                }
+            }
 		}
 	}
 

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java Sat Aug 26 04:39:20 2006
@@ -28,8 +28,9 @@
 import java.io.UTFDataFormatException;
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.iapi.services.i18n.MessageService;
+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.iapi.types.TypeId;
 
 /**
 	Converts a java.io.Reader to the on-disk UTF8 format used by Derby
@@ -38,8 +39,6 @@
 public final class ReaderToUTF8Stream
 	extends InputStream
 {
-    public static final int UNKNOWN_LENGTH = Integer.MIN_VALUE;
-
     /**
      * Application's reader wrapped in a LimitReader.
      */
@@ -59,8 +58,8 @@
      for clobs,varchar,char.
      If zero, no characters are truncated.
      */
-    private int charsToTruncate;
-    private static final char SPACE =' ';
+    private final int charsToTruncate;
+    private static final char SPACE = ' ';
     
     /**
      * Length of the final value, after truncation if any,
@@ -70,27 +69,71 @@
      information about the column width.
     */
     private final int valueLength; 
+    /** The maximum allowed length of the stream. */
+    private final int maximumLength;
+    /** The type name for the column data is inserted into. */
+    private final String typeName;
     
     /**
-     * Create a stream with truncation.
+     * Create a stream that will truncate trailing blanks if required/allowed.
+     *
+     * If the stream must be truncated, the number of blanks to truncate
+     * is specified to allow the stream to be checked for exact length, as
+     * required by JDBC 3.0. If the stream is shorter or longer than specified,
+     * an exception is thrown during read.
+     *
+     * @param appReader application reader
+     * @param valueLength the length of the reader in characters
+     * @param numCharsToTruncate the number of trailing blanks to truncate
+     * @param typeName type name of the column data is inserted into
      */
- 	public ReaderToUTF8Stream(Reader appReader, int valueLength,int numCharsToTruncate)
-	{
+    public ReaderToUTF8Stream(Reader appReader,
+                              int valueLength,
+                              int numCharsToTruncate,
+                              String typeName) {
         this.reader = new LimitReader(appReader);
-        if (valueLength != UNKNOWN_LENGTH) {
-            reader.setLimit(valueLength);
-        } 
-        if (SanityManager.DEBUG && valueLength == UNKNOWN_LENGTH) {
-            // Number of chars to truncate must be 0 if length is unknown.
-            // This count is used to check if the stream matches the
-            // specified length.
-            SanityManager.ASSERT(numCharsToTruncate == 0);
-        }
+        reader.setLimit(valueLength);
         buffer = new byte[BUFSIZE];
         blen = -1;        
         this.charsToTruncate = numCharsToTruncate;
         this.valueLength = valueLength;
-	}
+        this.maximumLength = -1;
+        this.typeName = typeName;
+    }
+
+    /**
+     * Create a UTF-8 stream for a length less application reader.
+     *
+     * A limit is placed on the length of the reader. If the reader exceeds
+     * the maximum length, truncation of trailing blanks is attempted. If
+     * truncation fails, an exception is thrown.
+     *
+     * @param appReader application reader
+     * @param maximumLength maximum allowed length in number of characters for
+     *      the reader
+     * @param typeName type name of the column data is inserted into
+     * @throws IllegalArgumentException if maximum length is negative, or type
+     *      name is <code>null<code>
+     */
+    public ReaderToUTF8Stream(Reader appReader,
+                              int maximumLength,
+                              String typeName) {
+        if (maximumLength < 0) {
+            throw new IllegalArgumentException("Maximum length for a capped " +
+                    "stream cannot be negative: " + maximumLength);
+        }
+        if (typeName == null) {
+            throw new IllegalArgumentException("Type name cannot be null");
+        }
+        this.reader = new LimitReader(appReader);
+        reader.setLimit(maximumLength);
+        buffer = new byte[BUFSIZE];
+        blen = -1;
+        this.maximumLength = maximumLength;
+        this.typeName = typeName;
+        this.charsToTruncate = -1;
+        this.valueLength = -1;
+    }
 
     /**
      * read from stream; characters converted to utf-8 derby specific encoding.
@@ -228,11 +271,19 @@
 			checkSufficientData();
 	}
 
-	/**
-		JDBC 3.0 (from tutorial book) requires that an
-		input stream has the correct number of bytes in
-		the stream.
-	*/
+    /**
+     * Validate the length of the stream, take corrective action if allowed.
+     *
+     * JDBC 3.0 (from tutorial book) requires that an input stream has the
+     * correct number of bytes in the stream.
+     * If the stream is too long, trailing blank truncation is attempted if
+     * allowed. If truncation fails, or is disallowed, an exception is thrown.
+     *
+     * @throws IOException if an errors occurs in the application stream
+     * @throws DerbyIOException if Derby finds a problem with the stream;
+     *      stream is too long and cannot be truncated, or the stream length
+     *      does not match the specified length
+     */
 	private void checkSufficientData() throws IOException
 	{
 		// now that we finished reading from the stream; the amount
@@ -240,54 +291,41 @@
         if (charsToTruncate > 0)
         {
             reader.setLimit(charsToTruncate);
-            int c = 0;
-            for (;;)
-            {
-                c = reader.read();
-                
-                if (c < 0)
-                {
-                    break;
-                }
-                else if (c != SPACE)
-                {
-                    // [NOTE] The assumption that this is always a Clob is not
-                    //        enforced anywhere (i.e. that 'charsToTruncate'
-                    //        is 0 for all other types)
-                    // throw truncation error, wont happen here for any other 
-                    // type except for clob
-                    // hence using TypeId.CLOB_NAME to avoid having to store
-                    // the type information along with this stream.
-                    throw new IOException(
-                        MessageService.getTextMessage(
-                            SQLState.LANG_STRING_TRUNCATION,
-                            TypeId.CLOB_NAME, 
-                            "XXXX", 
-                            String.valueOf(valueLength)));
-                }
-            }
+            truncate();
         }
         
+        // A length less stream that is capped, will return 0 even if there
+        // are more bytes in the application stream.
         int remainingBytes = reader.clearLimit();
-		if (remainingBytes > 0)
-			throw new IOException(MessageService.getTextMessage(SQLState.SET_STREAM_INEXACT_LENGTH_DATA));
+        if (remainingBytes > 0 && valueLength > 0) {
+            // If we had a specified length, throw exception.
+            throw new DerbyIOException(
+                    MessageService.getTextMessage(
+                        SQLState.SET_STREAM_INEXACT_LENGTH_DATA),
+                    SQLState.SET_STREAM_INEXACT_LENGTH_DATA);
+        }
 
 		// if we had a limit try reading one more character.
-		// JDBC 3.0 states the stream muct have the correct number of 
+		// JDBC 3.0 states the stream must have the correct number of
         // characters in it.
-
-		if (remainingBytes == 0) {
-			int c;
-			try
-			{
-				c = reader.read();
-               
-			}
-			catch (IOException ioe) {
-				c = -1;
-			}
-			if (c >= 0)
-				throw new IOException(MessageService.getTextMessage(SQLState.SET_STREAM_INEXACT_LENGTH_DATA));
+        if (remainingBytes == 0 && reader.read() >= 0) {
+            if (valueLength > -1) {
+                throw new DerbyIOException(
+                        MessageService.getTextMessage(
+                            SQLState.SET_STREAM_INEXACT_LENGTH_DATA),
+                        SQLState.SET_STREAM_INEXACT_LENGTH_DATA);
+            } else {
+                // Stream was capped (length less) and too long.
+                // Try to truncate if allowed, or else throw exception.
+                if (canTruncate()) {
+                    truncate();
+                } else {
+                    throw new DerbyIOException(
+                            MessageService.getTextMessage(
+                                SQLState.LANG_STRING_TRUNCATION),
+                            SQLState.LANG_STRING_TRUNCATION);
+                }
+            }
         }
 		
 		// can put the correct length into the stream.
@@ -306,6 +344,42 @@
 			buffer[blen++] = (byte) 0x00;
 		}
 	}
+
+    /**
+     * Determine if trailing blank truncation is allowed.
+     */
+    private boolean canTruncate() {
+        // Only a few types can be truncated, default is to not allow.
+        if (typeName.equals(TypeId.CLOB_NAME)) {
+            return true;
+        } else if (typeName.equals(TypeId.VARCHAR_NAME)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Attempt to truncate the stream by removing trailing blanks.
+     */
+    private void truncate()
+            throws IOException {
+        int c = 0;
+        for (;;) {
+            c = reader.read();
+
+            if (c < 0) {
+                break;
+            } else if (c != SPACE) {
+                throw new DerbyIOException(
+                    MessageService.getTextMessage(
+                        SQLState.LANG_STRING_TRUNCATION,
+                        typeName, 
+                        "XXXX", 
+                        String.valueOf(valueLength)),
+                    SQLState.LANG_STRING_TRUNCATION);
+            }
+        }
+    }
 
     /**
      * return resources 

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SQLClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SQLClob.java?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SQLClob.java (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SQLClob.java Sat Aug 26 04:39:20 2006
@@ -317,7 +317,7 @@
                 throw this.outOfRange();
             
             setValue(new ReaderToUTF8Stream(vc.getCharacterStream(),
-                    (int) vcl, 0), (int) vcl);
+                    (int) vcl, 0, TypeId.CLOB_NAME), (int) vcl);
             
         } catch (SQLException e) {
             throw dataTypeConversion("DAN-438-tmp");

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedPreparedStatement.java Sat Aug 26 04:39:20 2006
@@ -733,8 +733,7 @@
         */
         if (!lengthLess && length > Integer.MAX_VALUE)
                throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
-                  preparedStatement.getParameterTypes()
-                  [parameterIndex-1].getSQLstring());
+                                     getParameterSQLType(parameterIndex));
 
         try {
             ReaderToUTF8Stream utfIn;
@@ -784,19 +783,19 @@
                     }
                 }
                 // Create a stream with truncation.
-                utfIn = new ReaderToUTF8Stream(reader,
-                                               usableLength,
-                                               truncationLength);
+                utfIn = new ReaderToUTF8Stream(reader, usableLength,
+                        truncationLength, getParameterSQLType(parameterIndex));
             } else {
-                // Create a stream without exactness checks and truncation.
-                utfIn = new ReaderToUTF8Stream(reader,
-                                            ReaderToUTF8Stream.UNKNOWN_LENGTH,
-                                            0);
+                // Create a stream without exactness checks,
+                // but with a maximum limit.
+                utfIn = new ReaderToUTF8Stream(reader, colWidth,
+                                getParameterSQLType(parameterIndex));
             }
 
             // JDBC is one-based, DBMS is zero-based.
             // Note that for lengthless stream, usableLength will be
-            // Integer.MIN_VALUE. This is okay, based on the observation that
+            // the maximum length for the column. 
+            // This is okay, based on the observation that
             // setValue does not use the value for anything at all.
             pvs.getParameterForSet(
                 parameterIndex - 1).setValue(utfIn, usableLength);
@@ -898,13 +897,20 @@
         }
 
         try {
-            // If stream is lengthless, force length to -1 to get the expected
-            // behavior in RawToBinaryFormatStream.
+            RawToBinaryFormatStream rawStream;
             if (lengthLess) {
+                // Force length to -1 for good measure.
                 length = -1;
+                DataTypeDescriptor dtd[] = 
+                    preparedStatement.getParameterTypes();
+                rawStream = new RawToBinaryFormatStream(x,
+                        dtd[parameterIndex -1].getMaximumWidth(),
+                        dtd[parameterIndex -1].getTypeName());
+            } else {
+                rawStream = new RawToBinaryFormatStream(x, (int)length);
             }
             getParms().getParameterForSet(parameterIndex - 1).setValue(
-                    new RawToBinaryFormatStream(x, (int)length), (int)length);
+                    rawStream, (int)length);
 
 		} catch (StandardException t) {
 			throw EmbedResultSet.noStateChangeException(t);
@@ -1503,6 +1509,19 @@
 
 		return type;
 	}
+
+    /**
+     * Return the SQL type name for the parameter.
+     *
+     * @param parameterIndex the 1-based index of the parameter
+     * @return SQL name of the parameter
+     * @throws SQLException if parameter is out of range
+     */
+    protected final String getParameterSQLType(int parameterIndex)
+            throws SQLException {
+        DataTypeDescriptor[] pTypes = getTypes(parameterIndex);
+        return pTypes[parameterIndex-1].getTypeName();
+    }
 
     /**
      * Set the scale of a parameter.

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java Sat Aug 26 04:39:20 2006
@@ -2846,6 +2846,7 @@
     private void updateBinaryStreamInternal(int columnIndex, InputStream x,
                 final boolean lengthLess, long length, String updateMethodName)
             throws SQLException {
+        RawToBinaryFormatStream rawStream;
         if (!lengthLess) {
             if (length < 0)
                 throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
@@ -2857,14 +2858,18 @@
                 throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
                         getColumnSQLType(columnIndex));
             }
+            rawStream = new RawToBinaryFormatStream(x, (int)length);
         } else {
             // Force length to -1 if stream is length less.
             length = -1;
+            rawStream = new RawToBinaryFormatStream(x,
+                    getMaxColumnWidth(columnIndex),
+                    getColumnSQLType(columnIndex));
         }
 
         try {
 			getDVDforColumnToBeUpdated(columnIndex, updateMethodName).setValue(
-                    new RawToBinaryFormatStream(x, (int) length), (int) length);
+                    rawStream, (int) length);
 		} catch (StandardException t) {
 			throw noStateChangeException(t);
 		}
@@ -2987,8 +2992,7 @@
                 if (getColumnType(columnIndex) == Types.CLOB) {
                     // Need column width to figure out if truncation is
                     // needed
-                    int colWidth = resultDescription.getColumnDescriptor(
-                            columnIndex).getType().getMaximumWidth();
+                    int colWidth = getMaxColumnWidth(columnIndex);
 
                     // It is possible that the length of the stream passed in
                     // is greater than the column width, in which case the data
@@ -3002,13 +3006,16 @@
                     }
                 }
 
-                utfIn = new ReaderToUTF8Stream(
-                            reader, usableLength, truncationLength);
+                utfIn = new ReaderToUTF8Stream(reader, usableLength,
+                        truncationLength, getColumnSQLType(columnIndex));
             } else {
+                int colWidth = getMaxColumnWidth(columnIndex);
                 utfIn = new ReaderToUTF8Stream(
-                            reader, ReaderToUTF8Stream.UNKNOWN_LENGTH, 0);
+                            reader, colWidth, getColumnSQLType(columnIndex));
             }
 
+            // NOTE: The length argument to setValue is not used. If that
+            //       changes, the value might also have to change.
             getDVDforColumnToBeUpdated(columnIndex, updateMethodName).setValue(
                     utfIn, (int) usableLength);
         } catch (StandardException t) {
@@ -4514,6 +4521,20 @@
     {
         return resultDescription.getColumnDescriptor(column)
                        .getType().getTypeId().getSQLTypeName();
+    }
+
+    /**
+     * Return the user-defined maximum size of the column.
+     *
+     * Note that this may be different from the maximum column size Derby is
+     * able, or allowed, to handle (called 'maximum maximum length').
+     *
+     * @param columnIndex the 1-based index of the column
+     * @return the maximum length of the column
+     */
+    private final int getMaxColumnWidth(int columnIndex) {
+        return resultDescription.getColumnDescriptor(columnIndex).
+                    getType().getMaximumWidth();
     }
 
 	private final SQLException dataTypeConversion(String targetType, int column) {

Modified: db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/characterStreams.out
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/characterStreams.out?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/characterStreams.out (original)
+++ db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/characterStreams.out Sat Aug 26 04:39:20 2006
@@ -4,11 +4,11 @@
 MORE BYTES IN STREAM THAN PASSED IN VALUE
 MORE BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS BYTES IN STREAM THAN PASSED IN VALUE
 LESS BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 NULL ASCII STREAM
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
@@ -28,11 +28,11 @@
 MORE BYTES IN STREAM THAN PASSED IN VALUE
 MORE BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS BYTES IN STREAM THAN PASSED IN VALUE
 LESS BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 NULL ASCII STREAM
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
@@ -52,11 +52,11 @@
 MORE BYTES IN STREAM THAN PASSED IN VALUE
 MORE BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS BYTES IN STREAM THAN PASSED IN VALUE
 LESS BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 NULL ASCII STREAM
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
@@ -74,10 +74,10 @@
 Test setCharacterStream into CHAR
 MORE CHARACTERS IN READER THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS CHARACTERS IN READER STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
 13         |A Mississippi Republican |24         |NULL                     |NULL       |NULL                     |NULL       
@@ -86,10 +86,10 @@
 Test setCharacterStream into VARCHAR
 MORE CHARACTERS IN READER THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS CHARACTERS IN READER STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
 18         |NULL                     |NULL       |A Mississippi Republican |24         |NULL                     |NULL       
@@ -98,10 +98,10 @@
 Test setCharacterStream into LONG VARCHAR
 MORE CHARACTERS IN READER THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS CHARACTERS IN READER STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
 23         |NULL                     |NULL       |NULL                     |NULL       |A Mississippi Republican |24         
@@ -187,11 +187,11 @@
 MORE BYTES IN STREAM THAN PASSED IN VALUE
 MORE BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS BYTES IN STREAM THAN PASSED IN VALUE
 LESS BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 NULL ASCII STREAM
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
@@ -211,11 +211,11 @@
 MORE BYTES IN STREAM THAN PASSED IN VALUE
 MORE BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS BYTES IN STREAM THAN PASSED IN VALUE
 LESS BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 NULL ASCII STREAM
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
@@ -235,11 +235,11 @@
 MORE BYTES IN STREAM THAN PASSED IN VALUE
 MORE BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XSDA4) An unexpected exception was thrown
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS BYTES IN STREAM THAN PASSED IN VALUE
 LESS BYTES IN ASCII STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XSDA4) An unexpected exception was thrown
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 NULL ASCII STREAM
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
@@ -257,10 +257,10 @@
 Test setCharacterStream into CHAR
 MORE CHARACTERS IN READER THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS CHARACTERS IN READER STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
 13         |A Mississippi Republican |24         |NULL                     |NULL       |NULL                     |NULL       
@@ -269,10 +269,10 @@
 Test setCharacterStream into VARCHAR
 MORE CHARACTERS IN READER THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS CHARACTERS IN READER STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XCL30) An IOException was thrown when reading a 'java.sql.String' from an InputStream.
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
 18         |NULL                     |NULL       |A Mississippi Republican |24         |NULL                     |NULL       
@@ -281,10 +281,10 @@
 Test setCharacterStream into CLOB
 MORE CHARACTERS IN READER THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XSDA4) An unexpected exception was thrown
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 LESS CHARACTERS IN READER STREAM THAN SPECIFIED LENGTH - REJECTED 
 EXPECTED SQL Exception: (XSDA4) An unexpected exception was thrown
-EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQL Exception: (XJ001) Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 ID         |C                        |CLEN       |VC                       |VCLEN      |LVC                      |LVCLEN     
 -----------------------------------------------------------------------------------------------------------------------------
 23         |NULL                     |NULL       |NULL                     |NULL       |A Mississippi Republican |24         

Modified: db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out (original)
+++ db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out Sat Aug 26 04:39:20 2006
@@ -10,9 +10,9 @@
 len=56
 number of reads=56
 EXPECTED SQLSTATE(XSDA4): An unexpected exception was thrown
-EXPECTED SQLSTATE(XJ001): Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQLSTATE(XJ001): Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 EXPECTED SQLSTATE(XSDA4): An unexpected exception was thrown
-EXPECTED SQLSTATE(XJ001): Java exception: 'Input stream did not have exact amount of data as the requested length.: java.io.IOException'.
+EXPECTED SQLSTATE(XJ001): Java exception: 'Input stream did not have exact amount of data as the requested length.: org.apache.derby.iapi.services.io.DerbyIOException'.
 Test of getAsciiStream
 U+0041U+0042U+0043U+0044U+0045U+0046U+0047U+00c0U+00c1U+00c2U+00c3U+00c4U+00c5U+00ffU+003fU+003fU+003fU+003fU+003fU+003f
 U+0041U+0042U+0043U+0044U+0045U+0046U+0047U+00c0U+00c1U+00c2U+00c3U+00c4U+00c5U+00ffU+0100U+3042U+3044U+3046U+3048U+304a

Modified: db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out (original)
+++ db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out Sat Aug 26 04:39:20 2006
@@ -111,10 +111,10 @@
 No truncation and hence no error.
 ===> testing(using setAsciiStream) extin/char32675.data length = 32675
 EXPECTED SQLSTATE(XSDA4): An unexpected exception was thrown
-EXPECTED SQLSTATE(XJ001): Java exception: 'A truncation error was encountered trying to shrink CLOB 'XXXX' to length 32672.: java.io.IOException'.
+EXPECTED SQLSTATE(XJ001): Java exception: 'A truncation error was encountered trying to shrink CLOB 'XXXX' to length 32672.: org.apache.derby.iapi.services.io.DerbyIOException'.
 ===> testing(using setCharacterStream) extin/char32675.data length = 32675
 EXPECTED SQLSTATE(XSDA4): An unexpected exception was thrown
-EXPECTED SQLSTATE(XJ001): Java exception: 'A truncation error was encountered trying to shrink CLOB 'XXXX' to length 32672.: java.io.IOException'.
+EXPECTED SQLSTATE(XJ001): Java exception: 'A truncation error was encountered trying to shrink CLOB 'XXXX' to length 32672.: org.apache.derby.iapi.services.io.DerbyIOException'.
 ===> testing trailing non-blanks(using setString) length = 32675
 expected exception for data > 32672 in length
 ===> testing trailing non-blanks(using setObject) length = 32675

Modified: db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java (original)
+++ db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java Sat Aug 26 04:39:20 2006
@@ -24,32 +24,70 @@
 import junit.framework.*;
 
 import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.BaseJDBCTestSetup;
+import org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetStream;
 
 import java.io.*;
 import java.sql.*;
 import javax.sql.*;
 
+import org.apache.derby.iapi.services.io.DerbyIOException;
+import org.apache.derby.impl.jdbc.EmbedSQLException;
+
 /**
  * This class is used to test JDBC4 specific methods in the PreparedStatement(s)
  * object.
+ *
+ * A number of methods and variables are in place to aid the writing of tests:
+ * <ul><li>setBinaryStreamOnBlob
+ *     <li>setAsciiStream
+ *     <li>key - an id. One is generated each time setUp is run.
+ *     <li>reqeustKey() - generate a new unique id.
+ *     <li>psInsertX - prepared statements for insert.
+ *     <li>psFetchX - prepared statements for fetching values.
+ * </ul>
+ *
+ * For table creation, see the <code>suite</code>-method.
  */
 public class PreparedStatementTest extends BaseJDBCTestCase {
 
+    private static final String BLOBTBL = "BlobTestTable";
+    private static final String CLOBTBL = "ClobTestTable";
+    private static final String LONGVARCHAR = "LongVarcharTestTable";
+
+    /** Key used to id data inserted into the database. */
+    private static int globalKey = 1;
+
     /** Byte array passed in to the database. **/
     private static final byte[] BYTES = {
         0x65, 0x66, 0x67, 0x68, 0x69,
         0x69, 0x68, 0x67, 0x66, 0x65
     };
 
-    /**
-     * Default connection and prepared statements that are used by the tests.
+    // Default connection and prepared statements that are used by the tests.
+    /** 
+     * Default key to use for insertions.
+     * Is unique for each fixture. More keys can be fetched by calling
+     * <link>requestKey</link>.
      */
-    //Connection object
-    //PreparedStatement object
+    private int key;
+    /** Default connection object. */
+    /** PreparedStatement object with no positional arguments. */
     private PreparedStatement ps = null;
+    /** PreparedStatement to fetch BLOB with specified id. */
+    private PreparedStatement psFetchBlob = null;
+    /** PreparedStatement to insert a BLOB with specified id. */
+    private PreparedStatement psInsertBlob = null;
+    /** PreparedStatement to fetch CLOB with specified id. */
+    private PreparedStatement psFetchClob = null;
+    /** PreparedStatement to insert a CLOB with specified id. */
+    private PreparedStatement psInsertClob = null;
+    /** PreparedStatement to insert a LONG VARCHAR with specified id. */
+    private PreparedStatement psInsertLongVarchar = null;
     //Statement object
     private Statement s = null;
 
+
     
     /**
      * Create a test with the given name.
@@ -69,7 +107,8 @@
      */
     public void setUp() 
         throws SQLException {
-       //create the statement object
+        key = requestKey();
+        //create the statement object
         s = createStatement();
         //Create the PreparedStatement that will then be used as the basis 
         //throughout this test henceforth
@@ -77,14 +116,17 @@
         //setClob and setBlob
         ps = prepareStatement("select count(*) from sys.systables");
         
-         // STEP1: create the tables
-         // Structure of table
-         // --------------------------
-         // SNO            Clob Column
-         // --------------------------
-
-         s.execute("create table ClobTestTable (sno int, clobCol CLOB(1M))");
-         s.execute("create table BlobTestTable (sno int, blobCol BLOB(1M))");
+        // Prepare misc statements.
+        psFetchBlob = prepareStatement("SELECT dBlob FROM " +
+                BLOBTBL + " WHERE sno = ?");
+        psInsertBlob = prepareStatement("INSERT INTO " + BLOBTBL +
+                " VALUES (?, ?)");
+        psFetchClob = prepareStatement("SELECT dClob FROM " +
+                CLOBTBL + " WHERE sno = ?");
+        psInsertClob = prepareStatement("INSERT INTO " + CLOBTBL +
+                " VALUES (?, ?)");
+        psInsertLongVarchar = prepareStatement("INSERT INTO " + LONGVARCHAR +
+                " VALUES (?, ?)");
     }
 
     /**
@@ -97,9 +139,11 @@
     public void tearDown() 
         throws Exception {
         
-        s.execute("drop table ClobTestTable");
-        s.execute("drop table BlobTestTable");
-        s.close();
+        psFetchBlob.close();
+        psFetchClob.close();
+        psInsertBlob.close();
+        psInsertClob.close();
+        psInsertLongVarchar.close();
         
         super.tearDown();
     }
@@ -108,7 +152,48 @@
         TestSuite suite = new TestSuite();
         suite.addTestSuite(PreparedStatementTest.class);
         suite.addTest(SetObjectUnsupportedTest.suite(false));
-        return suite;
+        return new BaseJDBCTestSetup(suite) {
+                public void setUp()
+                        throws java.lang.Exception {
+                        try {
+                            create();
+                        } catch (SQLException sqle) {
+                            if (sqle.getSQLState().equals("X0Y32")) {
+                                drop();
+                                create();
+                            } else {
+                                throw sqle;
+                            }
+                        }
+                }
+
+                public void tearDown()
+                        throws java.lang.Exception {
+                    drop();
+                    super.tearDown();
+                }
+
+                private void create()
+                        throws SQLException {
+                    Statement stmt = getConnection().createStatement();
+                    stmt.execute("create table " + BLOBTBL +
+                            " (sno int, dBlob BLOB(1M))");
+                    stmt.execute("create table " + CLOBTBL +
+                            " (sno int, dClob CLOB(1M))");
+                    stmt.execute("create table " + LONGVARCHAR  +
+                            " (sno int, dLongVarchar LONG VARCHAR)");
+                    stmt.close();
+                }
+
+                private void drop()
+                        throws SQLException {
+                    Statement stmt = getConnection().createStatement();
+                    stmt.execute("drop table " + BLOBTBL);
+                    stmt.execute("drop table " + CLOBTBL);
+                    stmt.execute("drop table " + LONGVARCHAR);
+                    stmt.close();
+                }
+            };
     }
     
     //--------------------------------------------------------------------------
@@ -321,30 +406,31 @@
         StringReader is = new StringReader("Test data for the Clob object");
         is.reset();
         
-        PreparedStatement ps_sc = prepareStatement("insert into ClobTestTable values(?,?)");
-        
         //initially insert the data
-        ps_sc.setInt(1,1);
-        ps_sc.setClob(2,is,str.length());
-        ps_sc.executeUpdate();
+        psInsertClob.setInt(1, key);
+        psInsertClob.setClob(2, is, str.length());
+        psInsertClob.executeUpdate();
         
         //Now query to retrieve the Clob
-        ResultSet rs = s.executeQuery("select * from ClobTestTable where sno = 1");
+        psFetchClob.setInt(1, key);
+        ResultSet rs = psFetchClob.executeQuery();
         rs.next();
-        Clob clobToBeInserted = rs.getClob(2);
+        Clob clobToBeInserted = rs.getClob(1);
         rs.close();
         
         //Now use the setClob method
-        ps_sc.setInt(1,2);
-        ps_sc.setClob(2,clobToBeInserted);
-        ps_sc.execute();
+        int secondKey = requestKey();
+        psInsertClob.setInt(1, secondKey);
+        psInsertClob.setClob(2, clobToBeInserted);
+        psInsertClob.execute();
         
-        ps_sc.close();
+        psInsertClob.close();
         
         //Now test to see that the Clob has been stored correctly
-        rs = s.executeQuery("select * from ClobTestTable where sno = 2");
+        psFetchClob.setInt(1, secondKey);
+        rs = psFetchClob.executeQuery();
         rs.next();
-        Clob clobRetrieved = rs.getClob(2);
+        Clob clobRetrieved = rs.getClob(1);
         
         assertEquals(clobToBeInserted,clobRetrieved);
     }
@@ -363,25 +449,24 @@
         // Insert test data.
         String testString = "Test string for setCharacterStream\u1A00";
         Reader reader = new StringReader(testString);
-        PreparedStatement psChar = prepareStatement(
-                "insert into ClobTestTable values (?,?)");
-        psChar.setInt(1, 1);
-        psChar.setCharacterStream(2, reader);
-        psChar.execute();
+        psInsertClob.setInt(1, key);
+        psInsertClob.setCharacterStream(2, reader);
+        psInsertClob.execute();
         reader.close();
         // Must fetch Clob from database because we don't support
         // Connection.createClob on the client yet.
-        ResultSet rs = s.executeQuery(
-                "select clobCol from ClobTestTable where sno = 1");
+        psFetchClob.setInt(1, key);
+        ResultSet rs = psFetchClob.executeQuery();
         assertTrue("No results retrieved", rs.next());
+        int secondKey = requestKey();
         Clob insertClob = rs.getClob(1);
-        psChar.setInt(1, 2);
-        psChar.setClob(2, insertClob);
-        psChar.execute();
+        psInsertClob.setInt(1, secondKey);
+        psInsertClob.setClob(2, insertClob);
+        psInsertClob.execute();
 
         // Read back test data from database.
-        rs = s.executeQuery(
-                "select clobCol from ClobTestTable where sno = 2");
+        psFetchClob.setInt(1, secondKey);
+        rs = psFetchClob.executeQuery();
         assertTrue("No results retrieved", rs.next());
         Clob clobRetrieved = rs.getClob(1);
 
@@ -403,30 +488,31 @@
         InputStream is = new java.io.ByteArrayInputStream(BYTES);
         is.reset();
         
-        PreparedStatement ps_sb = prepareStatement("insert into BlobTestTable values(?,?)");
-        
         //initially insert the data
-        ps_sb.setInt(1,1);
-        ps_sb.setBlob(2,is,BYTES.length);
-        ps_sb.executeUpdate();
+        psInsertBlob.setInt(1, key);
+        psInsertBlob.setBlob(2, is, BYTES.length);
+        psInsertBlob.executeUpdate();
         
         //Now query to retrieve the Blob
-        ResultSet rs = s.executeQuery("select * from BlobTestTable where sno = 1");
+        psFetchBlob.setInt(1, key);
+        ResultSet rs = psFetchBlob.executeQuery();
         rs.next();
-        Blob blobToBeInserted = rs.getBlob(2);
+        Blob blobToBeInserted = rs.getBlob(1);
         rs.close();
         
         //Now use the setBlob method
-        ps_sb.setInt(1,2);
-        ps_sb.setBlob(2,blobToBeInserted);
-        ps_sb.execute();
+        int secondKey = requestKey();
+        psInsertBlob.setInt(1, secondKey);
+        psInsertBlob.setBlob(2, blobToBeInserted);
+        psInsertBlob.execute();
         
-        ps_sb.close();
+        psInsertBlob.close();
         
         //Now test to see that the Blob has been stored correctly
-        rs = s.executeQuery("select * from BlobTestTable where sno = 2");
+        psFetchBlob.setInt(1, secondKey);
+        rs = psFetchBlob.executeQuery();
         rs.next();
-        Blob blobRetrieved = rs.getBlob(2);
+        Blob blobRetrieved = rs.getBlob(1);
         
         assertEquals(blobToBeInserted, blobRetrieved);
     }
@@ -444,25 +530,24 @@
             throws IOException, SQLException {
         // Insert test data.
         InputStream is = new ByteArrayInputStream(BYTES);
-        PreparedStatement psByte = prepareStatement(
-                "insert into BlobTestTable values (?,?)");
-        psByte.setInt(1, 1);
-        psByte.setBinaryStream(2, is);
-        psByte.execute();
+        psInsertBlob.setInt(1, key);
+        psInsertBlob.setBinaryStream(2, is);
+        psInsertBlob.execute();
         is.close();
         // Must fetch Blob from database because we don't support
         // Connection.createBlob on the client yet.
-        ResultSet rs = s.executeQuery(
-                "select blobCol from BlobTestTable where sno = 1");
+        psFetchBlob.setInt(1, key);
+        ResultSet rs = psFetchBlob.executeQuery();
         assertTrue("No results retrieved", rs.next());
         Blob insertBlob = rs.getBlob(1);
-        psByte.setInt(1, 2);
-        psByte.setBlob(2, insertBlob);
-        psByte.execute();
+        int secondKey = requestKey();
+        psInsertBlob.setInt(1, secondKey);
+        psInsertBlob.setBlob(2, insertBlob);
+        psInsertBlob.execute();
 
         // Read back test data from database.
-        rs = s.executeQuery(
-                "select blobCol from BlobTestTable where sno = 2");
+        psFetchBlob.setInt(1, secondKey);
+        rs = psFetchBlob.executeQuery();
         assertTrue("No results retrieved", rs.next());
         Blob blobRetrieved = rs.getBlob(1);
 
@@ -547,23 +632,22 @@
         
         is.reset();
         
-        PreparedStatement ps_sc = prepareStatement("insert into ClobTestTable values(?,?)");
-        
         //initially insert the data
-        ps_sc.setInt(1,1);
-        ps_sc.setCharacterStream(2,is,str.length());
-        ps_sc.executeUpdate();
+        psInsertClob.setInt(1, key);
+        psInsertClob.setCharacterStream(2, is, str.length());
+        psInsertClob.executeUpdate();
         
         //Now query to retrieve the Clob
-        ResultSet rs = s.executeQuery("select * from ClobTestTable where sno = 1");
+        psFetchClob.setInt(1, key);
+        ResultSet rs = psFetchClob.executeQuery();
         rs.next();
-        Clob clobRetrieved = rs.getClob(2);
+        Clob clobRetrieved = rs.getClob(1);
         rs.close();
         
         String str_out = clobRetrieved.getSubString(1L,(int)clobRetrieved.length());
         
         assertEquals("Error in inserting data into the Clob object",str,str_out);
-        ps_sc.close();
+        psInsertClob.close();
     }
 
     public void testSetCharacterStreamLengthless()
@@ -571,16 +655,14 @@
         // Insert test data.
         String testString = "Test string for setCharacterStream\u1A00";
         Reader reader = new StringReader(testString);
-        PreparedStatement psChar = prepareStatement(
-                "insert into ClobTestTable values (?,?)");
-        psChar.setInt(1, 1);
-        psChar.setCharacterStream(2, reader);
-        psChar.execute();
+        psInsertClob.setInt(1, key);
+        psInsertClob.setCharacterStream(2, reader);
+        psInsertClob.execute();
         reader.close();
 
         // Read back test data from database.
-        ResultSet rs = s.executeQuery(
-                "select clobCol from ClobTestTable where sno = 1");
+        psFetchClob.setInt(1, key);
+        ResultSet rs = psFetchClob.executeQuery();
         assertTrue("No results retrieved", rs.next());
         Clob clobRetrieved = rs.getClob(1);
 
@@ -606,17 +688,16 @@
         
         is.reset();
         
-        PreparedStatement ps_sb = prepareStatement("insert into ClobTestTable values(?,?)");
-        
         //initially insert the data
-        ps_sb.setInt(1,1);
-        ps_sb.setAsciiStream(2,is,BYTES.length);
-        ps_sb.executeUpdate();
+        psInsertClob.setInt(1, key);
+        psInsertClob.setAsciiStream(2, is, BYTES.length);
+        psInsertClob.executeUpdate();
         
         //Now query to retrieve the Clob
-        ResultSet rs = s.executeQuery("select * from ClobTestTable where sno = 1");
+        psFetchClob.setInt(1, key);
+        ResultSet rs = psFetchClob.executeQuery();
         rs.next();
-        Clob ClobRetrieved = rs.getClob(2);
+        Clob ClobRetrieved = rs.getClob(1);
         rs.close();
         
         try {
@@ -628,23 +709,21 @@
         for(int i=0;i<BYTES.length;i++) {
             assertEquals("Error in inserting data into the Clob",BYTES[i],bytes1[i]);
         }
-        ps_sb.close();
+        psInsertClob.close();
     }
 
     public void testSetAsciiStreamLengthless()
             throws IOException, SQLException {
         // Insert test data.
         InputStream is = new ByteArrayInputStream(BYTES);
-        PreparedStatement psAscii = prepareStatement(
-                "insert into ClobTestTable values (?,?)");
-        psAscii.setInt(1, 1);
-        psAscii.setAsciiStream(2, is);
-        psAscii.execute();
+        psInsertClob.setInt(1, key);
+        psInsertClob.setAsciiStream(2, is);
+        psInsertClob.execute();
         is.close();
 
         // Read back test data from database.
-        ResultSet rs = s.executeQuery(
-                "select clobCol from ClobTestTable where sno = 1");
+        psFetchClob.setInt(1, key);
+        ResultSet rs = psFetchClob.executeQuery();
         assertTrue("No results retrieved", rs.next());
         Clob clobRetrieved = rs.getClob(1);
 
@@ -660,7 +739,7 @@
 
         // Cleanup
         isRetrieved.close();
-        psAscii.close();
+        psInsertClob.close();
     }
 
     /**
@@ -680,17 +759,16 @@
         
         is.reset();
         
-        PreparedStatement ps_sb = prepareStatement("insert into BlobTestTable values(?,?)");
-        
         //initially insert the data
-        ps_sb.setInt(1,1);
-        ps_sb.setBinaryStream(2,is,BYTES.length);
-        ps_sb.executeUpdate();
+        psInsertBlob.setInt(1, key);
+        psInsertBlob.setBinaryStream(2, is, BYTES.length);
+        psInsertBlob.executeUpdate();
         
         //Now query to retrieve the Clob
-        ResultSet rs = s.executeQuery("select * from BlobTestTable where sno = 1");
+        psFetchBlob.setInt(1, key);
+        ResultSet rs = psFetchBlob.executeQuery();
         rs.next();
-        Blob blobRetrieved = rs.getBlob(2);
+        Blob blobRetrieved = rs.getBlob(1);
         rs.close();
         
         try {
@@ -703,23 +781,21 @@
         for(int i=0;i<BYTES.length;i++) {
             assertEquals("Error in inserting data into the Blob",BYTES[i],bytes1[i]);
         }
-        ps_sb.close();
+        psInsertBlob.close();
     }
 
     public void testSetBinaryStreamLengthless()
             throws IOException, SQLException {
         // Insert test data.
         InputStream is = new ByteArrayInputStream(BYTES);
-        PreparedStatement psBinary = prepareStatement(
-                "insert into BlobTestTable values (?,?)");
-        psBinary.setInt(1, 1);
-        psBinary.setBinaryStream(2, is);
-        psBinary.execute();
+        psInsertBlob.setInt(1, key);
+        psInsertBlob.setBinaryStream(2, is);
+        psInsertBlob.execute();
         is.close();
 
         // Read back test data from database.
-        ResultSet rs = s.executeQuery(
-                "select blobCol from BlobTestTable where sno = 1");
+        psFetchBlob.setInt(1, key);
+        ResultSet rs = psFetchBlob.executeQuery();
         assertTrue("No results retrieved", rs.next());
         Blob blobRetrieved = rs.getBlob(1);
 
@@ -735,6 +811,363 @@
 
         // Cleanup
         isRetrieved.close();
-        psBinary.close();
+        psInsertBlob.close();
+    }
+
+    public void testSetBinaryStreamLengthLess1KOnBlob()
+            throws IOException, SQLException {
+        int length = 1*1024;
+        setBinaryStreamOnBlob(key, length, -1, 0, true);
+        psFetchBlob.setInt(1, key);
+        ResultSet rs = psFetchBlob.executeQuery();
+        assertTrue("Empty resultset", rs.next());
+        assertEquals(new LoopingAlphabetStream(length),
+                     rs.getBinaryStream(1));
+        assertFalse("Resultset should have been exhausted", rs.next());
+        rs.close();
+    }
+
+    public void testSetBinaryStreamLengthLess32KOnBlob()
+            throws IOException, SQLException {
+        int length = 32*1024;
+        setBinaryStreamOnBlob(key, length, -1, 0, true);
+        psFetchBlob.setInt(1, key);
+        ResultSet rs = psFetchBlob.executeQuery();
+        assertTrue("Empty resultset", rs.next());
+        assertEquals(new LoopingAlphabetStream(length),
+                     rs.getBinaryStream(1));
+        assertFalse("Resultset should have been exhausted", rs.next());
+        rs.close();
+    }
+
+    public void testSetBinaryStreamLengthLess65KOnBlob()
+            throws IOException, SQLException {
+        int length = 65*1024;
+        setBinaryStreamOnBlob(key, length, -1, 0, true);
+        psFetchBlob.setInt(1, key);
+        ResultSet rs = psFetchBlob.executeQuery();
+        assertTrue("Empty resultset", rs.next());
+        LoopingAlphabetStream s1 = new LoopingAlphabetStream(length);
+        assertEquals(new LoopingAlphabetStream(length),
+                     rs.getBinaryStream(1));
+        assertFalse("Resultset should have been exhausted", rs.next());
+        rs.close();
+    }
+
+    public void testSetBinaryStreamLengthLessOnBlobTooLong() {
+        int length = 1*1024*1024+512;
+        try {
+            setBinaryStreamOnBlob(key, length, -1, 0, true);
+        } catch (SQLException sqle) {
+            if (usingEmbedded()) {
+                assertSQLState("XCL30", sqle);
+            } else {
+                assertSQLState("22001", sqle);
+            }
+        }
+    }
+
+    public void testExceptionPathOnePage_bs()
+            throws SQLException {
+        int length = 11;
+        try {
+            setBinaryStreamOnBlob(key, length -1, length, 0, false);
+            fail("Inserted a BLOB with fewer bytes than specified");
+        } catch (SQLException sqle) {
+            if (usingEmbedded()) {
+                assertSQLState("XSDA4", sqle);
+            } else {
+                assertSQLState("XN017", sqle);
+            }
+        }
+    }
+
+    public void testExceptionPathMultiplePages_bs()
+            throws SQLException {
+        int length = 1*1024*1024;
+        try {
+            setBinaryStreamOnBlob(key, length -1, length, 0, false);
+            fail("Inserted a BLOB with fewer bytes than specified");
+        } catch (SQLException sqle) {
+            if (usingEmbedded()) {
+                assertSQLState("XSDA4", sqle);
+            } else {
+                assertSQLState("XN017", sqle);
+            }
+        }
+    }
+
+    public void testBlobExceptionDoesNotRollbackOtherStatements()
+            throws IOException, SQLException {
+        getConnection().setAutoCommit(false);
+        int[] keys = {key, requestKey(), requestKey()};
+        for (int i=0; i < keys.length; i++) {
+            psInsertBlob.setInt(1, keys[i]);
+            psInsertBlob.setNull(2, Types.BLOB);
+            assertEquals(1, psInsertBlob.executeUpdate());
+        }
+        // Now insert a BLOB that fails because the stream is too short.
+        int failedKey = requestKey();
+        int length = 1*1024*1024;
+        try {
+            setBinaryStreamOnBlob(failedKey, length -1, length, 0, false);
+            fail("Inserted a BLOB with less data than specified");
+        } catch (SQLException sqle) {
+            if (usingEmbedded()) {
+                assertSQLState("XSDA4", sqle);
+            } else {
+                assertSQLState("XN017", sqle);
+            }
+        }
+        // Now make sure the previous statements are there, and that the last
+        // BLOB is not.
+        ResultSet rs;
+        for (int i=0; i < keys.length; i++) {
+            psFetchBlob.setInt(1, keys[i]);
+            rs = psFetchBlob.executeQuery();
+            assertTrue(rs.next());
+            assertFalse(rs.next());
+            rs.close();
+        }
+        psFetchBlob.setInt(1, failedKey);
+        rs = psFetchBlob.executeQuery();
+        // When using the Derby client driver, the data seems to be padded
+        // with 0s and inserted... Thus, the select returns a row.
+        if (!usingEmbedded()) {
+            assertTrue(rs.next());
+            InputStream is = rs.getBinaryStream(1);
+            int lastByte = -1;
+            int b = 99; // Just a value > 0.
+            while (b > -1) {
+                lastByte = b;
+                b = is.read();
+            }
+            assertEquals("Last padded byte is not 0", 0, lastByte);
+        }
+        assertFalse(rs.next());
+        rs.close();
+        rollback();
+        // Make sure all data is gone after the rollback.
+        for (int i=0; i < keys.length; i++) {
+            psFetchBlob.setInt(1, keys[i]);
+            rs = psFetchBlob.executeQuery();
+            assertFalse(rs.next());
+            rs.close();
+        }
+        // Make sure the failed insert has not "reappeared" somehow...
+        psFetchBlob.setInt(1, failedKey);
+        rs = psFetchBlob.executeQuery();
+        assertFalse(rs.next());
+
+    }
+
+    public void testSetAsciiStreamLengthLess1KOnClob()
+            throws IOException, SQLException {
+        int length = 1*1024;
+        setAsciiStream(psInsertClob, key, length, -1, 0, true);
+        psFetchClob.setInt(1, key);
+        ResultSet rs = psFetchClob.executeQuery();
+        assertTrue("Empty resultset", rs.next());
+        assertEquals(new LoopingAlphabetStream(length),
+                     rs.getAsciiStream(1));
+        assertFalse("Resultset should have been exhausted", rs.next());
+        rs.close();
+    }
+
+    public void testSetAsciiStreamLengthLess32KOnClob()
+            throws IOException, SQLException {
+        int length = 32*1024;
+        setAsciiStream(psInsertClob, key, length, -1, 0, true);
+        psFetchClob.setInt(1, key);
+        ResultSet rs = psFetchClob.executeQuery();
+        assertTrue("Empty resultset", rs.next());
+        assertEquals(new LoopingAlphabetStream(length),
+                     rs.getAsciiStream(1));
+        assertFalse("Resultset should have been exhausted", rs.next());
+        rs.close();
+    }
+
+    public void testSetAsciiStreamLengthLess65KOnClob()
+            throws IOException, SQLException {
+        int length = 65*1024;
+        setAsciiStream(psInsertClob, key, length, -1, 0, true);
+        psFetchClob.setInt(1, key);
+        ResultSet rs = psFetchClob.executeQuery();
+        assertTrue("Empty resultset", rs.next());
+        assertEquals(new LoopingAlphabetStream(length),
+                     rs.getAsciiStream(1));
+        assertFalse("Resultset should have been exhausted", rs.next());
+        rs.close();
+    }
+
+    public void testSetAsciiStreamLengthLessOnClobTooLong() {
+        int length = 1*1024*1024+512;
+        try {
+            setAsciiStream(psInsertClob, key, length, -1, 0, true);
+        } catch (SQLException sqle) {
+            if (usingEmbedded()) {
+                assertSQLState("XSDA4", sqle);
+            } else {
+                assertSQLState("22001", sqle);
+            }
+        }
+    }
+
+    public void testSetAsciiStreamLengthLessOnClobTooLongTruncate()
+            throws SQLException {
+        int trailingBlanks = 512;
+        int length = 1*1024*1024 + trailingBlanks;
+        setAsciiStream(psInsertClob, key, length, -1, trailingBlanks, true);
+    }
+
+    public void testSetAsciiStreamLengthlessOnLongVarCharTooLong() {
+        int length = 32700+512;
+        try {
+            setAsciiStream(psInsertLongVarchar, key, length, -1, 0, true);
+            fail("Inserted a LONG VARCHAR that is too long");
+        } catch (SQLException sqle) {
+            if (usingEmbedded()) {
+                assertInternalDerbyIOExceptionState("XCL30", "22001", sqle);
+            } else {
+                assertSQLState("22001", sqle);
+            }
+        }
+    }
+
+    public void testSetAsciiStreamLengthlessOnLongVarCharDontTruncate() {
+        int trailingBlanks = 2000;
+        int length = 32000 + trailingBlanks;
+        try {
+            setAsciiStream(psInsertLongVarchar, key, length, -1,
+                    trailingBlanks, true);
+            fail("Truncation is not allowed for LONG VARCHAR");
+        } catch (SQLException sqle) {
+            if (usingEmbedded()) {
+                assertInternalDerbyIOExceptionState("XCL30", "22001", sqle);
+            } else {
+                assertSQLState("22001", sqle);
+            }
+        }
+    }
+
+    /************************************************************************
+     *                 A U X I L I A R Y  M E T H O D S                     *
+     ************************************************************************/
+
+    /**
+     * Insert data into a Blob column with setBinaryStream.
+     *
+     * @param id unique id for inserted row
+     * @param actualLength the actual length of the stream
+     * @param specifiedLength the specified length of the stream
+     * @param trailingBlanks number of characters at the end that is blank
+     * @param lengthLess whether to use the length less overloads or not
+     */
+    private void setBinaryStreamOnBlob(int id,
+                                       int actualLength,
+                                       int specifiedLength,
+                                       int trailingBlanks,
+                                       boolean lengthLess)
+            throws SQLException {
+        psInsertBlob.setInt(1, id);
+        if (lengthLess) {
+            psInsertBlob.setBinaryStream(2, new LoopingAlphabetStream(
+                                                actualLength,
+                                                trailingBlanks));
+        } else {
+            psInsertBlob.setBinaryStream(2,
+                               new LoopingAlphabetStream(
+                                        actualLength,
+                                        trailingBlanks),
+                               specifiedLength);
+        }
+        assertEquals("Insert with setBinaryStream failed",
+                1, psInsertBlob.executeUpdate());
+    }
+
+    /**
+     * Insert data into a column with setAsciiStream.
+     * The prepared statement passed must have two positional parameters;
+     * one int and one more. Depending on the last parameter, the execute
+     * might succeed or it might fail. This is intended behavior, and should
+     * be handled by the caller. For instance, calling this method on an
+     * INT-column would fail, calling it on a CLOB-column would succeed.
+     *
+     * @param id unique id for inserted row
+     * @param actualLength the actual length of the stream
+     * @param specifiedLength the specified length of the stream
+     * @param trailingBlanks number of characters at the end that is blank
+     * @param lengthLess whether to use the length less overloads or not
+     */
+    private void setAsciiStream(PreparedStatement ps,
+                                int id,
+                                int actualLength,
+                                int specifiedLength,
+                                int trailingBlanks,
+                                boolean lengthLess)
+            throws SQLException {
+        ps.setInt(1, id);
+        if (lengthLess) {
+            ps.setAsciiStream(2, 
+                              new LoopingAlphabetStream(
+                                                actualLength,
+                                                trailingBlanks));
+        } else {
+            ps.setAsciiStream(2,
+                              new LoopingAlphabetStream(
+                                                actualLength,
+                                                trailingBlanks),
+                              specifiedLength);
+        }
+        assertEquals("Insert with setAsciiStream failed",
+                1, ps.executeUpdate());
+    }
+
+    /**
+     * Get next key to id inserted data with.
+     */
+    private static int requestKey() {
+        return globalKey++;
+    }
+
+    /**
+     * Return the last chained SQLException.
+     * If there are no exceptions chained, the original one is returned.
+     */
+    private SQLException getLastSQLException(SQLException sqle) {
+        SQLException last = sqle;
+        SQLException next = sqle;
+        while (next != null) {
+            last = next;
+            next = last.getNextException();
+        }
+        return last;
+    }
+
+    /**
+     * This methods is not to be used, but sometimes you have to!
+     *
+     * @param preSQLState the expected outer SQL state
+     * @param expectedInternal the expected internal SQL state
+     * @param sqle the outer SQLException
+     */
+    private void assertInternalDerbyIOExceptionState(
+                                        String preSQLState,
+                                        String expectedInternal,
+                                        SQLException sqle) {
+        assertSQLState("Outer/public SQL state incorrect",
+                       preSQLState, sqle);
+        // We need to dig a little with the current way exceptions are
+        // being reported. We can use getCause because we always run with
+        // Mustang/Java SE 6.
+        Throwable cause = getLastSQLException(sqle).getCause();
+        assertTrue("Exception not an EmbedSQLException",
+                   cause instanceof EmbedSQLException);
+        cause = ((EmbedSQLException)cause).getJavaException();
+        assertTrue("Exception not a DerbyIOException",
+                   cause instanceof DerbyIOException);
+        DerbyIOException dioe = (DerbyIOException)cause;
+        assertEquals("Incorrect internal SQL state", expectedInternal,
+                     dioe.getSQLState());
     }
 }

Modified: db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/build.xml?rev=437146&r1=437145&r2=437146&view=diff
==============================================================================
--- db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/build.xml (original)
+++ db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/util/build.xml Sat Aug 26 04:39:20 2006
@@ -62,6 +62,7 @@
       </classpath>
       <include name="${this.dir}/*.java"/> 
       <include name="${this.dir}/StaticInitializers/*.java"/> 
+      <include name="${this.dir}/streams/*.java"/> 
       <include name="${this.dir}/corruptio/*.java"/>
       <exclude name="${this.dir}/XATestUtil.java"/>
     </javac>