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 10:44:01 UTC

svn commit: r437127 [1/2] - 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/functionTests/master/ testing/org/apache/derbyTes...

Author: kahatlen
Date: Sat Aug 26 01:43:58 2006
New Revision: 437127

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

This patch adds the capability to handle length less streams
properly. Such streams are now capped at the maximum length of the
column it is inserted into. If the stream is longer than the limit, a
DerbyIOException (subclass of IOException) is thrown, causing a
statement severity StandardException to be thrown. The exception
handling system takes care of cleaning up.

If a stream is shorter or longer than the specified length, an
exception is thrown as required by JDBC 3.0.

Contributed by Kristian Waagan.

Added:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/streams/
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/streams/ByteAlphabet.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/streams/CharAlphabet.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/streams/LoopingAlphabetReader.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/streams/LoopingAlphabetStream.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.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/SQLClob.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/functionTests/master/characterStreams.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/build.xml

Added: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java?rev=437127&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java Sat Aug 26 01:43:58 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/trunk/java/engine/org/apache/derby/iapi/services/io/DerbyIOException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.java?rev=437127&r1=437126&r2=437127&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/RawToBinaryFormatStream.java Sat Aug 26 01:43:58 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/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=437127&r1=437126&r2=437127&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 Sat Aug 26 01:43:58 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/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=437127&r1=437126&r2=437127&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 Sat Aug 26 01:43:58 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/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=437127&r1=437126&r2=437127&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 Sat Aug 26 01:43:58 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/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=437127&r1=437126&r2=437127&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 Sat Aug 26 01:43:58 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/trunk/java/testing/org/apache/derbyTesting/functionTests/master/characterStreams.out
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/characterStreams.out?rev=437127&r1=437126&r2=437127&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/characterStreams.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/characterStreams.out Sat Aug 26 01:43:58 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/trunk/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out?rev=437127&r1=437126&r2=437127&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/resultsetStream.out Sat Aug 26 01:43:58 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/trunk/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out?rev=437127&r1=437126&r2=437127&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out Sat Aug 26 01:43:58 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/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java?rev=437127&r1=437126&r2=437127&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/PreparedStatementTest.java Sat Aug 26 01:43:58 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/trunk/java/testing/org/apache/derbyTesting/functionTests/util/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/build.xml?rev=437127&r1=437126&r2=437127&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/build.xml (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/build.xml Sat Aug 26 01:43:58 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>