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/07/18 09:18:34 UTC

svn commit: r422995 [1/2] - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/types/ engine/org/apache/derby/impl/jdbc/ testing/org/apache/derbyTesting/functionTests/tests/jdbc4/

Author: kahatlen
Date: Tue Jul 18 00:18:33 2006
New Revision: 422995

URL: http://svn.apache.org/viewvc?rev=422995&view=rev
Log:
DERBY-1417 (partial) Add new, lengthless overloads to the streaming api

Tests and implementations for the following methods on the embedded side:
[ResultSet]
  public void updateAsciiStream(int columnIndex, InputStream x)
  public void updateBinaryStream(int columnIndex, InputStream x)
  public void updateCharacterStream(int columnIndex, Reader x)
  public void updateAsciiStream(String columnName, InputStream x)
  public void updateBinaryStream(String columnName, InputStream x)
  public void updateCharacterStream(String columnName, Reader reader)
  public void updateBlob(int columnIndex, InputStream x)
  public void updateBlob(String columnName, InputStream x)
  public void updateClob(int columnIndex, Reader x)
  public void updateClob(String columnName, Reader x)
[PreparedStatement]
  public void setBinaryStream(int parameterIndex, InputStream x)
  public void setAsciiStream(int parameterIndex, InputStream x)
  public void setCharacterStream(int parameterIndex, Reader reader)
  public void setClob(int parameterIndex, Reader reader)
  public void setBlob(int parameterIndex, InputStream inputStream)

*IMPORTANT*: This patch must be built with Mustang build 91 for the
 tests to compile!

Some of the tests are temporarily disabled for the client
driver. These will be enabed when the client implementation is
submitted.

Patch contributed by Kristian Waagan.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedCallableStatement40.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/tests/jdbc4/PreparedStatementTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ResultSetTest.java

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=422995&r1=422994&r2=422995&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 Tue Jul 18 00:18:33 2006
@@ -28,6 +28,7 @@
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.iapi.services.i18n.MessageService;
 import org.apache.derby.iapi.services.io.LimitReader;
+import org.apache.derby.iapi.services.sanity.SanityManager;
 
 /**
 	Converts a java.io.Reader to the on-disk UTF8 format used by Derby
@@ -36,6 +37,8 @@
 public final class ReaderToUTF8Stream
 	extends InputStream
 {
+    public static final int UNKNOWN_LENGTH = Integer.MIN_VALUE;
+
     /**
      * Application's reader wrapped in a LimitReader.
      */
@@ -73,7 +76,15 @@
  	public ReaderToUTF8Stream(Reader appReader, int valueLength,int numCharsToTruncate)
 	{
         this.reader = new LimitReader(appReader);
-        reader.setLimit(valueLength);
+        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);
+        }
         buffer = new byte[BUFSIZE];
         blen = -1;        
         this.charsToTruncate = numCharsToTruncate;
@@ -239,6 +250,9 @@
                 }
                 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

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedCallableStatement40.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedCallableStatement40.java?rev=422995&r1=422994&r2=422995&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedCallableStatement40.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedCallableStatement40.java Tue Jul 18 00:18:33 2006
@@ -160,6 +160,11 @@
         throw Util.notImplemented("setNString (int,value)");
     }
     
+    public void setNCharacterStream(int parameterIndex, Reader value)
+            throws SQLException {
+        throw Util.notImplemented();
+    }
+
     public void setNCharacterStream(int index, Reader value, long length) throws SQLException{
         throw Util.notImplemented ("setNCharacterStream (int, Reader, long)");
     }
@@ -167,7 +172,12 @@
     public void setNClob(int index, NClob value) throws SQLException{
         throw Util.notImplemented ("setNClob (int, NClob)");
     }
-    
+
+    public void setNClob(int parameterIndex, Reader reader)
+            throws SQLException {
+        throw Util.notImplemented();
+    }
+
     public void setNClob(int parameterIndex, Reader reader, long length)
     throws SQLException{
         throw Util.notImplemented ("setNClob(int,reader,length)");

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=422995&r1=422994&r2=422995&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 Tue Jul 18 00:18:33 2006
@@ -567,20 +567,8 @@
      */
     public final void setAsciiStream(int parameterIndex, InputStream x, long length)
 	    throws SQLException {
-		checkStatus();
-
-		int jdbcTypeId = getParameterJDBCType(parameterIndex);
-		
-		switch (jdbcTypeId) {
-		case Types.CHAR:
-		case Types.VARCHAR:
-		case Types.LONGVARCHAR:
-		case Types.CLOB:
-			break;
-		default:
-			throw dataTypeConversion(parameterIndex, "java.io.InputStream(ASCII)");
-		}
-
+        checkCharacterStreamConditions(parameterIndex,
+                                        "java.io.InputStream(ASCII)");
 		java.io.Reader r = null;
 
 		if (x != null)
@@ -594,7 +582,7 @@
 			}
 		}
 
-		setCharacterStream(parameterIndex, r, length);
+        setCharacterStreamInternal(parameterIndex, r, false, length);
 	}
 
     /**
@@ -629,8 +617,6 @@
 	}
 
     /**
-     * JDBC 2.0
-     *
      * When a very large UNICODE value is input to a LONGVARCHAR
      * parameter, it may be more practical to send it via a
      * java.io.Reader. JDBC will read the data from the stream
@@ -649,20 +635,8 @@
     public final void setCharacterStream(int parameterIndex,
        			  java.io.Reader reader,
 			  long length) throws SQLException {
-		checkStatus();
-		int jdbcTypeId = getParameterJDBCType(parameterIndex);
-		switch (jdbcTypeId) {
-		case Types.CHAR:
-		case Types.VARCHAR:
-		case Types.LONGVARCHAR:
-		case Types.CLOB:
-			break;
-		default:
-			throw dataTypeConversion(parameterIndex, "java.io.Reader");
-		}
-
-
-		setCharacterStreamInternal(parameterIndex, reader, length);
+        checkCharacterStreamConditions(parameterIndex, "java.io.Reader");
+        setCharacterStreamInternal(parameterIndex, reader, false, length);
 	}
 
     /**
@@ -687,14 +661,56 @@
         setCharacterStream(parameterIndex,reader,(long)length);
     }
 
+    /**
+     * Check general (pre)conditions for setXXXStream methods operating on
+     * character streams.
+     *
+     * @param parameterIndex 1-based index of the parameter.
+     * @param srcDataTypeDesc type description of the source data. Used in
+     *          error message for data type conversion failure.
+     */
+    private final void checkCharacterStreamConditions(int parameterIndex,
+                                                       String srcDataTypeDesc)
+            throws SQLException {
+        checkStatus();
+        int jdbcTypeId = getParameterJDBCType(parameterIndex);
+        switch (jdbcTypeId) {
+            case Types.CHAR:
+            case Types.VARCHAR:
+            case Types.LONGVARCHAR:
+            case Types.CLOB:
+                break;
+            default:
+                throw dataTypeConversion(parameterIndex, srcDataTypeDesc);
+        }
+    }
 
+    /**
+     * Set the given character stream for the specified parameter.
+     *
+     * If <code>lengthLess</code> is <code>true</code>, the following
+     * conditions are either not checked or verified at the execution time
+     * of the prepared statement:
+     * <ol><li>If the stream length is negative.
+     *     <li>If the stream's actual length equals the specified length.</ol>
+     * The <code>lengthLess</code> variable was added to differentiate between
+     * streams with invalid lengths and streams without known lengths.
+     *
+     * @param parameterIndex the 1-based index of the parameter to set.
+     * @param reader the data.
+     * @param lengthLess tells whether we know the length of the data or not.
+     * @param length the length of the data. Ignored if <code>lengthLess</code>
+     *          is <code>true</code>.
+     */
     private void setCharacterStreamInternal(int parameterIndex,
-						Reader reader, long length)
+                                            Reader reader,
+                                            final boolean lengthLess,
+                                            long length)
 	    throws SQLException
 	{
-        // check for -ve length
-        if (length < 0) 
-          throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
+        // Check for negative length if length is specified.
+        if (!lengthLess && length < 0)
+            throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
 
 	    int jdbcTypeId = getParameterJDBCType(parameterIndex);
 
@@ -710,56 +726,73 @@
            This checking needs to be done because currently derby does not
            support Clob sizes greater than 2G-1 
         */
-   	    if (length > Integer.MAX_VALUE)
+        if (!lengthLess && length > Integer.MAX_VALUE)
                throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
                   preparedStatement.getParameterTypes()
                   [parameterIndex-1].getSQLstring());
-        /*
-            We cast the length from long to int. This would'nt be appropriate if
-            the limit of 2G-1 is decided to be increased at a later stage. 
-        */
-        int intLength = (int)length;		    
-
-	    try {
-
-			ParameterValueSet pvs = getParms();
-            int usableLength = intLength;
-            int truncationLength = 0;
 
-            // Currently long varchar does not allow for truncation of trailing
-            // blanks.  
-            // For char and varchar types, current mechanism of materializing
-            // when using streams seems fine given their  max limits.
-            // This change is fix for DERBY-352: Insert of clobs using streams
-            // should not materialize the entire stream into memory
-            // In case of clobs, the truncation of trailing blanks is factored
-            // in when reading from the stream without materializing the entire
-            // stream, and so the special casing for clob below.
-            if (jdbcTypeId == Types.CLOB) 
-            {
-                // Need column width to figure out if truncation is needed 
-                DataTypeDescriptor dtd[] = preparedStatement
-                        .getParameterTypes();
-                int colWidth = dtd[parameterIndex - 1].getMaximumWidth();
-
-                // It is possible that the length of the stream passed in is
-                // greater than the column width, in which case the data from
-                // the stream needs to be truncated.
-                // usableLength is the length of the data from stream that can
-                // be inserted which is min(colWidth,length) provided 
-                // length - colWidth has trailing blanks only
-                // we have used intLength into which the length variable had
-                // been cast to an int and stored  
-                if (intLength > colWidth) {                 
-                    usableLength = colWidth;
-                    truncationLength = intLength - usableLength;
+        try {
+            ReaderToUTF8Stream utfIn;
+            ParameterValueSet pvs = getParms();
+            // Need column width to figure out if truncation is needed
+            DataTypeDescriptor dtd[] = preparedStatement
+                    .getParameterTypes();
+            int colWidth = dtd[parameterIndex - 1].getMaximumWidth();
+            // Default to max column width. This will be used to limit the
+            // amount of data read when operating on "lengthless" streams.
+            int usableLength = colWidth;
+
+            if (!lengthLess) {
+                // We cast the length from long to int. This wouldn't be
+                // appropriate if the limit of 2G-1 is decided to be increased
+                // at a later stage.
+                int intLength = (int)length;
+                int truncationLength = 0;
+
+                usableLength = intLength;
+
+                // Currently long varchar does not allow for truncation of
+                // trailing blanks.
+                // For char and varchar types, current mechanism of
+                // materializing when using streams seems fine given their max
+                // limits.
+                // This change is fix for DERBY-352: Insert of clobs using
+                // streams should not materialize the entire stream into memory
+                // In case of clobs, the truncation of trailing blanks is
+                // factored in when reading from the stream without
+                // materializing the entire stream, and so the special casing
+                // for clob below.
+                if (jdbcTypeId == Types.CLOB)
+                {
+
+                    // It is possible that the length of the stream passed in
+                    // is greater than the column width, in which case the data
+                    // from the stream needs to be truncated.
+                    // usableLength is the length of the data from stream that
+                    // can be inserted which is min(colWidth,length) provided
+                    // length - colWidth has trailing blanks only
+                    // we have used intLength into which the length variable had
+                    // been cast to an int and stored
+                    if (intLength > colWidth) {
+                        usableLength = colWidth;
+                        truncationLength = intLength - usableLength;
+                    }
                 }
+                // Create a stream with truncation.
+                utfIn = new ReaderToUTF8Stream(reader,
+                                               usableLength,
+                                               truncationLength);
+            } else {
+                // Create a stream without exactness checks and truncation.
+                utfIn = new ReaderToUTF8Stream(reader,
+                                            ReaderToUTF8Stream.UNKNOWN_LENGTH,
+                                            0);
             }
 
-            ReaderToUTF8Stream utfIn = new ReaderToUTF8Stream(
-                    reader, usableLength, truncationLength);
-
-            /* JDBC is one-based, DBMS is zero-based */
+            // 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
+            // setValue does not use the value for anything at all.
             pvs.getParameterForSet(
                 parameterIndex - 1).setValue(utfIn, usableLength);
 
@@ -769,6 +802,27 @@
 	}
 
     /**
+     * Sets the designated parameter to the given input stream.
+     * When a very large binary value is input to a <code>LONGVARBINARY</code>
+     * parameter, it may be more practical to send it via a
+     * <code>java.io.InputStream</code> object. The data will be read from the
+     * stream as needed until end-of-file is reached.
+     *
+     * <em>Note:</em> This stream object can either be a standard Java stream
+     * object or your own subclass that implements the standard interface.
+     *
+     * @param parameterIndex the first parameter is 1, the second is 2, ...
+     * @param x the java input stream which contains the binary parameter value
+     * @throws SQLException if a database access error occurs or this method is
+     *      called on a closed <code>PreparedStatement</code>
+     */
+    public void setBinaryStream(int parameterIndex, InputStream x)
+            throws SQLException {
+        checkBinaryStreamConditions(parameterIndex);
+        setBinaryStreamInternal(parameterIndex, x, true, -1);
+    }
+
+    /**
      * sets the parameter to the Binary stream
      * 
      * @param parameterIndex the first parameter is 1, the second is 2, ...
@@ -778,21 +832,8 @@
      */
     public final void setBinaryStream(int parameterIndex, InputStream x, long length)
 	    throws SQLException {
-
-		checkStatus();
-
-		int jdbcTypeId = getParameterJDBCType(parameterIndex);
-		switch (jdbcTypeId) {
-		case Types.BINARY:
-		case Types.VARBINARY:
-		case Types.LONGVARBINARY:
-		case Types.BLOB:
-			break;
-		default:
-			throw dataTypeConversion(parameterIndex, "java.io.InputStream");
-		}
-
-    	setBinaryStreamInternal(parameterIndex, x, length);
+        checkBinaryStreamConditions(parameterIndex);
+        setBinaryStreamInternal(parameterIndex, x, false, length);
 	}
 
     /**
@@ -808,12 +849,29 @@
         setBinaryStream(parameterIndex,x,(long)length);
     }
 
+    /**
+     * Set the given stream for the specified parameter.
+     *
+     * If <code>lengthLess</code> is <code>true</code>, the following
+     * conditions are either not checked or verified at the execution time
+     * of the prepared statement:
+     * <ol><li>If the stream length is negative.
+     *     <li>If the stream's actual length equals the specified length.</ol>
+     * The <code>lengthLess</code> variable was added to differentiate between
+     * streams with invalid lengths and streams without known lengths.
+     *
+     * @param parameterIndex the 1-based index of the parameter to set.
+     * @param x the data.
+     * @param lengthLess tells whether we know the length of the data or not.
+     * @param length the length of the data. Ignored if <code>lengthLess</code>
+     *          is <code>true</code>.
+     */
     private void setBinaryStreamInternal(int parameterIndex, InputStream x,
-				long length)
+                                         final boolean lengthLess, long length)
 	    throws SQLException
 	{
 
-        if ( length < 0 ) 
+        if ( !lengthLess && length < 0 )
             throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
         
 		int jdbcTypeId = getParameterJDBCType(parameterIndex);
@@ -828,21 +886,47 @@
         // For now, we cast the length from long to int as a result.
         // If we ever decide to increase these limits for lets say blobs, 
         // in that case the cast to int would not be appropriate.
-        if ( length > Integer.MAX_VALUE ) {
+        if ( !lengthLess && length > Integer.MAX_VALUE ) {
             throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
                getEmbedParameterSetMetaData().getParameterTypeName(
                    parameterIndex));
         }
 
         try {
-
-			getParms().getParameterForSet(parameterIndex - 1).setValue(new RawToBinaryFormatStream(x, (int)length), (int)length);
+            // If stream is lengthless, force length to -1 to get the expected
+            // behavior in RawToBinaryFormatStream.
+            if (lengthLess) {
+                length = -1;
+            }
+            getParms().getParameterForSet(parameterIndex - 1).setValue(
+                    new RawToBinaryFormatStream(x, (int)length), (int)length);
 
 		} catch (StandardException t) {
 			throw EmbedResultSet.noStateChangeException(t);
 		}
 	}
 
+    /**
+     * Check general (pre)conditions for setXXXStream methods operating on
+     * binary streams.
+     *
+     * @param parameterIndex 1-based index of the parameter.
+     */
+    private final void checkBinaryStreamConditions(int parameterIndex)
+            throws SQLException {
+        checkStatus();
+        int jdbcTypeId = getParameterJDBCType(parameterIndex);
+        switch (jdbcTypeId) {
+        case Types.BINARY:
+        case Types.VARBINARY:
+        case Types.LONGVARBINARY:
+        case Types.BLOB:
+            break;
+        default:
+            throw dataTypeConversion(parameterIndex, "java.io.InputStream");
+        }
+    }
+
 	/////////////////////////////////////////////////////////////////////////
 	//
 	//	JDBC 2.0	-	New public methods
@@ -1286,16 +1370,7 @@
     public void setBlob (int i, Blob x)
         throws SQLException
     {
-        checkStatus();
-        int colType;
-        synchronized (getConnectionSynchronization())
-        {
-            colType = getParameterJDBCType(i);
-        }
-		// DB2: only allow setBlob on a BLOB column.
-		if (colType != Types.BLOB)
-            throw dataTypeConversion(i, "java.sql.Blob");
-
+        checkBlobConditions(i);
 		if (x == null)
 			setNull(i, Types.BLOB);
 		else
@@ -1305,11 +1380,25 @@
             // will read from the stream and drain some part of the stream 
             // Hence the need to declare this local variable - streamLength
             long streamLength = x.length();
-            setBinaryStreamInternal(i, x.getBinaryStream(), streamLength);
+            setBinaryStreamInternal(i, x.getBinaryStream(), false,
+                    streamLength);
         }
 	}
 
     /**
+     * Check general (pre)conditions for setClob methods.
+     *
+     * @param parameterIndex 1-based index of the parameter.
+     */
+    private final void checkClobConditions(int parameterIndex)
+            throws SQLException {
+        checkStatus();
+        if (getParameterJDBCType(parameterIndex) != Types.CLOB) {
+            throw dataTypeConversion(parameterIndex, "java.sql.Clob");
+        }
+    }
+
+    /**
      * JDBC 2.0
      *
      * Set a CLOB parameter.
@@ -1320,17 +1409,7 @@
     public void setClob (int i, Clob x)
         throws SQLException
     {
-        checkStatus();
-        int colType;
-        synchronized (getConnectionSynchronization())
-        {
-            colType = getParameterJDBCType(i);
-        }
-
-		// DB2, only allow setClob on a CLOB column.
-		if (colType != Types.CLOB)
-            throw dataTypeConversion(i, "java.sql.Clob");
-
+        checkClobConditions(i);
 		if (x == null)
 			setNull(i, Types.CLOB);
 		else
@@ -1348,7 +1427,8 @@
             // Hence the need to declare this local variable - streamLength
             long streamLength = x.length();
 
-            setCharacterStreamInternal(i, x.getCharacterStream(), streamLength);
+            setCharacterStreamInternal(i, x.getCharacterStream(),
+                                       false, streamLength);
         }
         
 	}
@@ -1625,7 +1705,92 @@
 
    //jdbc 4.0 methods
 
-   
+    /**
+     * Sets the designated parameter to the given input stream.
+     * When a very large ASCII value is input to a <code>LONGVARCHAR</code>
+     * parameter, it may be more practical to send it via a
+     * <code>java.io.InputStream</code>. Data will be read from the stream as
+     * needed until end-of-file is reached. The JDBC driver will do any
+     * necessary conversion from ASCII to the database char format.
+     *
+     * <em>Note:</em> This stream object can either be a standard Java stream
+     * object or your own subclass that implements the standard interface.
+     *
+     * @param parameterIndex the first parameter is 1, the second is 2, ...
+     * @param x the Java input stream that contains the ASCII parameter value
+     * @throws SQLException if a database access error occurs or this method is
+     *      called on a closed <code>PreparedStatement</code>
+     */
+    public void setAsciiStream(int parameterIndex, InputStream x)
+            throws SQLException {
+        checkCharacterStreamConditions(parameterIndex,
+                                        "java.io.InputStream(ASCII)");
+        java.io.Reader asciiStream = null;
+
+        if (x != null) {
+            // Use ISO-8859-1 and not US-ASCII as JDBC seems to define
+            // ASCII as 8 bits. US-ASCII is 7.
+            try {
+                asciiStream = new java.io.InputStreamReader(x, "ISO-8859-1");
+            } catch (java.io.UnsupportedEncodingException uee) {
+                throw new SQLException(uee.getMessage());
+            }
+        }
+
+        setCharacterStreamInternal(parameterIndex, asciiStream, true, -1);
+    }
+
+    /**
+     * Sets the designated parameter to the given <code>Reader</code> object.
+     * When a very large UNICODE value is input to a LONGVARCHAR parameter, it
+     * may be more practical to send it via a <code>java.io.Reader</code>
+     * object. The data will be read from the stream as needed until
+     * end-of-file is reached. The JDBC driver will do any necessary conversion
+     * from UNICODE to the database char format.
+     *
+     * <em>Note:</em> This stream object can either be a standard Java stream
+     * object or your own subclass that implements the standard interface.
+     *
+     * Using this lengthless overload is not less effective than using one
+     * where the stream length is specified, but since there is no length
+     * specified, the exact length check will not be performed.
+     *
+     * @param parameterIndex the first parameter is 1, the second is 2, ...
+     * @param reader the <code>java.io.Reader</code> object that contains the
+     *      Unicode data
+     * @throws SQLException if a database access error occurs or this method is
+     *      called on a closed <code>PreparedStatement</code>
+     */
+    public void setCharacterStream(int parameterIndex, Reader reader)
+            throws SQLException {
+        checkCharacterStreamConditions(parameterIndex, "java.io.Reader");
+        setCharacterStreamInternal(parameterIndex, reader,
+                                   true, -1);
+    }
+
+    /**
+     * Sets the designated parameter to a <code>Reader</code> object.
+     * This method differs from the <code>setCharacterStream(int,Reader)</code>
+     * method because it informs the driver that the parameter value should be
+     * sent to the server as a <code>CLOB</code>. When the
+     * <code>setCharacterStream</code> method is used, the driver may have to
+     * do extra work to determine whether the parameter data should be sent to
+     * the server as a <code>LONGVARCHAR</code> or a <code>CLOB</code>.
+     *
+     * @param parameterIndex index of the first parameter is 1, the second is
+     *      2, ...
+     * @param reader an object that contains the data to set the parameter
+     *      value to.
+     * @throws SQLException if a database access error occurs, this method is
+     *      called on a closed PreparedStatementor if parameterIndex does not
+     *      correspond to a parameter marker in the SQL statement
+     */
+    public void setClob(int parameterIndex, Reader reader)
+            throws SQLException {
+        checkClobConditions(parameterIndex);
+        setCharacterStreamInternal(parameterIndex, reader, true, -1);
+    }
+
     /**
      * Sets the designated parameter to a Reader object.
      *
@@ -1640,15 +1805,32 @@
     
     public void setClob(int parameterIndex, Reader reader, long length)
     throws SQLException{
-        checkStatus();
-        int colType;
-        synchronized(getConnectionSynchronization()) {
-            colType = getParameterJDBCType(parameterIndex);
-            if(colType != Types.CLOB)
-                throw dataTypeConversion(parameterIndex, "java.sql.Clob");
-            
-            setCharacterStreamInternal(parameterIndex,reader,length);
-        }
+        checkClobConditions(parameterIndex);
+        setCharacterStreamInternal(parameterIndex, reader, false, length);
+    }
+
+    /**
+     * Sets the designated parameter to a <code>InputStream</code> object.
+     * This method differs from the <code>setBinaryStream(int, InputStream)
+     * </code>  method because it informs the driver that the parameter value
+     * should be sent to the server as a <code>BLOB</code>. When the
+     * <code>setBinaryStream</code> method is used, the driver may have to do
+     * extra work to determine whether the parameter data should be sent to the
+     * server as a <code>LONGVARBINARY</code> or a <code>BLOB</code>
+     *
+     * @param parameterIndex index of the first parameter is 1, the second is
+     *      2, ...
+     * @param inputStream an object that contains the data to set the parameter
+     *      value to.
+     * @throws SQLException if a database access error occurs, this method is
+     *      called on a closed <code>PreparedStatement</code> or if
+     *      <code>parameterIndex</code> does not correspond to a parameter
+     *      marker in the SQL statement
+     */
+    public void setBlob(int parameterIndex, InputStream inputStream)
+            throws SQLException {
+        checkBlobConditions(parameterIndex);
+        setBinaryStreamInternal(parameterIndex, inputStream, true, -1);
     }
 
     /**
@@ -1668,14 +1850,20 @@
     
     public void setBlob(int parameterIndex, InputStream inputStream, long length)
     throws SQLException{
+        checkBlobConditions(parameterIndex);
+        setBinaryStreamInternal(parameterIndex, inputStream, false, length);
+    }
+
+    /**
+     * Check general (pre)conditions for setBlob methods.
+     *
+     * @param parameterIndex 1-based index of the parameter.
+     */
+    private final void checkBlobConditions(int parameterIndex)
+            throws SQLException {
         checkStatus();
-        int colType;
-        synchronized (getConnectionSynchronization()) {
-            colType = getParameterJDBCType(parameterIndex);
-            if (colType != Types.BLOB)
-                throw dataTypeConversion(parameterIndex, "java.sql.Blob");
-            
-            setBinaryStreamInternal(parameterIndex,inputStream,length);
+        if (getParameterJDBCType(parameterIndex) != Types.BLOB) {
+            throw dataTypeConversion(parameterIndex, "java.sql.Blob");
         }
-    }        
+    }
 }

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=422995&r1=422994&r2=422995&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 Tue Jul 18 00:18:33 2006
@@ -2685,9 +2685,54 @@
 				throw new SQLException(uee.getMessage());
 			}
 		}
-		updateCharacterStream(columnIndex, r, length);
+		updateCharacterStreamInternal(columnIndex, r, false, length,
+				"updateAsciiStream");
 	}
 
+    /**
+     * Updates the designated column with a character stream value.
+     * The data will be read from the stream as needed until end-of-stream is
+     * reached.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the <code>updateRow</code> or </code>insertRow</code>
+     * methods are called to update the database.
+     *
+     * @param columnIndex the first column is 1, the second is 2, ...
+     * @param x the new column value
+     * @throws SQLException if the columnIndex is not valid; if a database
+     *      access error occurs; the result set concurrency is
+     *      <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *      result set
+     */
+    public void updateAsciiStream(int columnIndex, InputStream x)
+            throws SQLException {
+        checksBeforeUpdateXXX("updateAsciiStream", columnIndex);
+
+        int colType = getColumnType(columnIndex);
+        switch (colType) {
+            case Types.CHAR:
+            case Types.VARCHAR:
+            case Types.LONGVARCHAR:
+            case Types.CLOB:
+                break;
+            default:
+                throw dataTypeConversion(columnIndex, "java.io.InputStream");
+        }
+
+        java.io.Reader r = null;
+        if (x != null) {
+            try {
+                r = new java.io.InputStreamReader(x, "ISO-8859-1");
+            } catch (java.io.UnsupportedEncodingException uee) {
+                throw new SQLException(uee.getMessage());
+            }
+        }
+        updateCharacterStreamInternal(columnIndex, r, true, -1,
+                                      "updateAsciiStream");
+    }
+
 	/**
 	 *
 	 * Update a column with a binary stream value.
@@ -2726,24 +2771,84 @@
 			return;
 		}
 
-		updateBinaryStreamInternal(columnIndex, x, length,"updateBinaryStream");
+		updateBinaryStreamInternal(columnIndex, x, false, length,
+                                   "updateBinaryStream");
 	}
 
-	private void updateBinaryStreamInternal(int columnIndex,
-						java.io.InputStream x, long length, String updateMethodName)
-	    throws SQLException
-	{
-        if (length < 0)
-            throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
-        
-        // max number of bytes that can be set to be inserted 
-        // in Derby is 2Gb-1 (ie Integer.MAX_VALUE). 
-        // (e.g into a blob column).
-        if (length > Integer.MAX_VALUE ) {
-            throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
-                    getColumnSQLType(columnIndex));
+    /**
+     * Updates the designated column with a binary stream value.
+     * The data will be read from the stream as needed until end-of-stream is
+     * reached.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the <code>updateRow</code> or <code>insertRow</code>
+     * methods are called to update the database.
+     *
+     * @param columnIndex the first column is 1, the second is 2, ...
+     * @param x the new column value
+     * @throws SQLException if the columnLabel is not valid; if a database
+     *      access error occurs; the result set concurrency is
+     *      <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *      result set
+     */
+    public void updateBinaryStream(int columnIndex, InputStream x)
+            throws SQLException {
+        checksBeforeUpdateXXX("updateBinaryStream", columnIndex);
+        int colType = getColumnType(columnIndex);
+        switch (colType) {
+            case Types.BINARY:
+            case Types.VARBINARY:
+            case Types.LONGVARBINARY:
+            case Types.BLOB:
+                break;
+            default:
+                throw dataTypeConversion(columnIndex, "java.io.InputStream");
         }
-        
+        updateBinaryStreamInternal(columnIndex, x, true, -1,
+                                   "updateBinaryStream");
+    }
+
+    /**
+     * Set the given binary stream for the specified parameter.
+     *
+     * If <code>lengthLess</code> is <code>true</code>, the following
+     * conditions are either not checked or verified at the execution time
+     * of <code>updateRow</code>/<code>insertRow</code>:
+     * <ol><li>If the stream length is negative.
+     *     <li>If the stream's actual length equals the specified length.</ol>
+     * The <code>lengthLess</code> variable was added to differentiate between
+     * streams with invalid lengths and streams without known lengths.
+     *
+     * @param columnIndex the 1-based index of the parameter to set.
+     * @param x the data.
+     * @param lengthLess tells whether we know the length of the data or not.
+     * @param length the length of the data. Ignored if <code>lengthLess</code>
+     *          is <code>true</code>.
+     * @param updateMethodName the name of the method calling us. Used in
+     *      error messages.
+     * @throws SQLException if reading the data fails, or one of the data
+     *      checks fails.
+     */
+    private void updateBinaryStreamInternal(int columnIndex, InputStream x,
+                final boolean lengthLess, long length, String updateMethodName)
+            throws SQLException {
+        if (!lengthLess) {
+            if (length < 0)
+                throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
+
+            // max number of bytes that can be set to be inserted
+            // in Derby is 2Gb-1 (ie Integer.MAX_VALUE).
+            // (e.g into a blob column).
+            if (length > Integer.MAX_VALUE ) {
+                throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
+                        getColumnSQLType(columnIndex));
+            }
+        } else {
+            // Force length to -1 if stream is length less.
+            length = -1;
+        }
+
         try {
 			getDVDforColumnToBeUpdated(columnIndex, updateMethodName).setValue(
                     new RawToBinaryFormatStream(x, (int) length), (int) length);
@@ -2787,12 +2892,68 @@
 			default:
 				throw dataTypeConversion(columnIndex, "java.io.Reader");
 		}
-		updateCharacterStreamInternal(columnIndex, x, length, "updateCharacterStream");
+		updateCharacterStreamInternal(columnIndex, x, false, length,
+                                      "updateCharacterStream");
 	}
 
-    private void updateCharacterStreamInternal(int columnIndex,
-						java.io.Reader reader, long length, String updateMethodName)
-	    throws SQLException
+    /**
+     * Updates the designated column with a character stream value.
+     * The data will be read from the stream as needed until end-of-stream is
+     * reached.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the <code>updateRow</code> or </code>insertRow</code>
+     * methods are called to update the database.
+     *
+     * @param columnIndex the first column is 1, the second is 2, ...
+     * @param x the new column value
+     * @throws SQLException if the columnIndex is not valid; if a database
+     *      access error occurs; the result set concurrency is
+     *      <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *      result set
+     */
+    public void updateCharacterStream(int columnIndex, Reader x)
+            throws SQLException {
+        checksBeforeUpdateXXX("updateCharacterStream", columnIndex);
+        int colType = getColumnType(columnIndex);
+        switch (colType) {
+            case Types.CHAR:
+            case Types.VARCHAR:
+            case Types.LONGVARCHAR:
+            case Types.CLOB:
+                break;
+            default:
+                throw dataTypeConversion(columnIndex, "java.io.Reader");
+        }
+        updateCharacterStreamInternal(columnIndex, x, true, -1,
+                                      "updateCharacterStream");
+    }
+
+    /**
+     * Set the given character stream for the specified parameter.
+     *
+     * If <code>lengthLess</code> is <code>true</code>, the following
+     * conditions are either not checked or verified at the execution time
+     * of the prepared statement:
+     * <ol><li>If the stream length is negative.
+     *     <li>If the stream's actual length equals the specified length.</ol>
+     * The <code>lengthLess</code> variable was added to differentiate between
+     * streams with invalid lengths and streams without known lengths.
+     *
+     * @param columnIndex the 1-based index of the parameter to set.
+     * @param reader the data.
+     * @param lengthLess tells whether we know the length of the data or not.
+     * @param length the length of the data. Ignored if <code>lengthLess</code>
+     *          is <code>true</code>.
+     * @throws SQLException if reading the data fails, or one of the data
+     *      checks fails.
+     */
+    private void updateCharacterStreamInternal(int columnIndex, Reader reader,
+                                               final boolean lengthLess,
+                                               long length,
+                                               String updateMethodName)
+            throws SQLException
 	{
 		try {
 
@@ -2802,52 +2963,62 @@
                 return;
             }
             
-            // check for -ve length here 
-            if (length < 0) 
-                throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
+            ReaderToUTF8Stream utfIn;
+            int usableLength = -1;
+            if (!lengthLess) {
+                // check for -ve length here
+                if (length < 0)
+                    throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
+
+                // max number of characters that can be set to be inserted
+                // in Derby is 2Gb-1 (ie Integer.MAX_VALUE).
+                // (e.g into a CLOB column).
+                if (length > Integer.MAX_VALUE ) {
+                    throw newSQLException(
+                            SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
+                            getColumnSQLType(columnIndex));
+                }
 
-            // max number of characters that can be set to be inserted 
-            // in Derby is 2Gb-1 (ie Integer.MAX_VALUE). 
-            // (e.g into a CLOB column).
-            if (length > Integer.MAX_VALUE ) {
-                throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
-                        getColumnSQLType(columnIndex));
-            } 
-           
-            // length is +ve. at this point, all checks for negative
-            // length has already been done
-            int usableLength = (int) length;
-            int truncationLength = 0;
-
-            // Currently long varchar does not allow for truncation of
-            // trailing blanks.  For char and varchar types, current mechanism 
-            // of materializing when using streams seems fine given their max
-            // limits. This change is fix for DERBY-352: Insert of clobs using
-            // streams should not materialize the entire stream into memory
-            // In case of clobs, the truncation of trailing blanks is
-            // factored in when reading from the stream without materializing
-            // the entire stream, and so the special casing for clob below.
-            if (getColumnType(columnIndex) == Types.CLOB) {
-                // Need column width to figure out if truncation is
-                // needed
-                int colWidth = resultDescription.getColumnDescriptor(
-                        columnIndex).getType().getMaximumWidth();
-
-                // It is possible that the length of the stream passed
-                // in is greater than the column width, in which case the data
-                // from the stream needs to be truncated.
-                // usableLength is the length of the data from stream
-                // that can be used which is min(colWidth,length) provided
-                // length - colWidth has trailing blanks only
-                if (usableLength > colWidth) {                   
-                    truncationLength = usableLength - colWidth;
-                    usableLength = colWidth;
+                // length is +ve. at this point, all checks for negative
+                // length has already been done
+                usableLength = (int) length;
+                int truncationLength = 0;
+
+                // Currently long varchar does not allow for truncation of
+                // trailing blanks.  For char and varchar types, current
+                // mechanism of materializing when using streams seems fine
+                // given their max limits. This change is fix for DERBY-352:
+                // Insert of clobs using streams should not materialize the
+                // entire stream into memory
+                // In case of clobs, the truncation of trailing blanks is
+                // factored in when reading from the stream without
+                // materializing the entire stream, and so the special casing
+                // for clob below.
+                if (getColumnType(columnIndex) == Types.CLOB) {
+                    // Need column width to figure out if truncation is
+                    // needed
+                    int colWidth = resultDescription.getColumnDescriptor(
+                            columnIndex).getType().getMaximumWidth();
+
+                    // It is possible that the length of the stream passed in
+                    // is greater than the column width, in which case the data
+                    // from the stream needs to be truncated.
+                    // usableLength is the length of the data from stream
+                    // that can be used which is min(colWidth,length) provided
+                    // length - colWidth has trailing blanks only
+                    if (usableLength > colWidth) {
+                        truncationLength = usableLength - colWidth;
+                        usableLength = colWidth;
+                    }
                 }
+
+                utfIn = new ReaderToUTF8Stream(
+                            reader, usableLength, truncationLength);
+            } else {
+                utfIn = new ReaderToUTF8Stream(
+                            reader, ReaderToUTF8Stream.UNKNOWN_LENGTH, 0);
             }
 
-            ReaderToUTF8Stream utfIn = new ReaderToUTF8Stream(
-                    reader, usableLength, truncationLength);
-            
             getDVDforColumnToBeUpdated(columnIndex, updateMethodName).setValue(
                     utfIn, (int) usableLength);
         } catch (StandardException t) {
@@ -3938,7 +4109,8 @@
             updateNull(columnIndex);
         else {
             long length = x.length();
-            updateBinaryStreamInternal(columnIndex, x.getBinaryStream(), length, "updateBlob");
+            updateBinaryStreamInternal(columnIndex, x.getBinaryStream(), false,
+                                       length, "updateBlob");
         }
 	}
 
@@ -3993,7 +4165,8 @@
             long length = x.length();
 
             updateCharacterStreamInternal(
-                columnIndex, x.getCharacterStream(),length, "updateClob");
+                columnIndex, x.getCharacterStream(), false, length,
+                "updateClob");
         }
 	}
 
@@ -4522,6 +4695,31 @@
          updateAsciiStream(findColumnName(columnName),x,length);
      }
 
+    /**
+     * Updates the designated column with a character stream value.
+     * The data will be read from the stream as needed until end-of-stream is
+     * reached.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the <code>updateRow</code> or </code>insertRow</code>
+     * methods are called to update the database.
+     *
+     * @param columnName the label for the column specified with the SQL AS
+     *      clause. If the SQL AS clause was not specified, then the label is
+     *      the name of the column
+     * @param x the new column value
+     * @throws SQLException if the columnIndex is not valid; if a database
+     *      access error occurs; the result set concurrency is
+     *      <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *      result set
+     */
+    public void updateAsciiStream(String columnName, InputStream x)
+            throws SQLException {
+        checkIfClosed("updateAsciiStream");
+        updateAsciiStream(findColumnName(columnName), x);
+    }
+
      /**
       *
       * JDBC 4.0
@@ -4549,6 +4747,31 @@
          updateBinaryStream(findColumnName(columnName),x,length);
      }
 
+    /**
+     * Updates the designated column with a binary stream value.
+     * The data will be read from the stream as needed until end-of-stream is
+     * reached.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the <code>updateRow</code> or <code>insertRow</code>
+     * methods are called to update the database.
+     *
+     * @param columnName the label for the column specified with the SQL AS
+     *      clause. If the SQL AS clause was not specified, then the label is
+     *      the name of the column
+     * @param x the new column value
+     * @throws SQLException if the columnLabel is not valid; if a database
+     *      access error occurs; the result set concurrency is
+     *      <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *      result set
+     */
+    public void updateBinaryStream(String columnName, InputStream x)
+            throws SQLException {
+        checkIfClosed("updateBinaryStream");
+        updateBinaryStream(findColumnName(columnName), x);
+    }
+
      /**
       * JDBC 4.0
       *
@@ -4574,6 +4797,31 @@
          updateCharacterStream(findColumnName(columnName),reader,length);
      }
 
+    /**
+     * Updates the designated column with a character stream value.
+     * The data will be read from the stream as needed until end-of-stream is
+     * reached.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the <code>updateRow</code> or </code>insertRow</code>
+     * methods are called to update the database.
+     *
+     * @param columnName the label for the column specified with the SQL AS
+     *      clause. If the SQL AS clause was not specified, then the label is
+     *      the name of the column
+     * @param reader the new column value
+     * @throws SQLException if the columnIndex is not valid; if a database
+     *      access error occurs; the result set concurrency is
+     *      <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *      result set
+     */
+    public void updateCharacterStream(String columnName, Reader reader)
+            throws SQLException {
+        checkIfClosed("updateCharacterStream");
+        updateCharacterStream(findColumnName(columnName), reader);
+    }
+
      /**
       *
       * JDBC 4.0
@@ -4602,10 +4850,38 @@
          if (x == null)
              updateNull(columnIndex);
          else {
-             updateBinaryStreamInternal(columnIndex, x, length, "updateBlob");
+             updateBinaryStreamInternal(columnIndex, x, false, length,
+                                        "updateBlob");
          }
      }
 
+    /**
+     * Updates the designated column using the given input stream.
+     * The data will be read from the stream as needed until end-of-stream is reached.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the updateRow or insertRow methods are called to
+     * update the database.
+     *
+     * @param columnIndex the first column is 1, the second is 2, ...
+     * @param x an object that contains the data to set the
+     *     parameter value to.
+     * @throws SQLException if the columnIndex is not valid; if a database
+     *     access error occurs; the result set concurrency is
+     *     <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *     result set
+     */
+    public void updateBlob(int columnIndex, InputStream x)
+           throws SQLException {
+       checksBeforeUpdateXXX("updateBlob", columnIndex);
+       int colType = getColumnType(columnIndex);
+       if (colType != Types.BLOB) {
+            throw dataTypeConversion(columnIndex, "java.sql.Blob");
+       }
+       updateBinaryStreamInternal(columnIndex, x, true, -1, "updateBlob");
+    }
+
      /**
       *
       * JDBC 4.0
@@ -4631,6 +4907,31 @@
          updateBlob(findColumnName(columnName),x,length);
      }
 
+    /**
+     * Updates the designated column using the given input stream.
+     * The data will be read from the stream as needed until end-of-stream is reached.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the updateRow or insertRow methods are called to
+     * update the database.
+     *
+     * @param columnName the label for the column specified with the SQL AS
+     *     clause. If the SQL AS clause was not specified, then the label is
+     *     the name of the column
+     * @param x an object that contains the data to set the
+     *     parameter value to.
+     * @throws SQLException if the columnIndex is not valid; if a database
+     *     access error occurs; the result set concurrency is
+     *     <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *     result set
+     */
+    public void updateBlob(String columnName, InputStream x)
+           throws SQLException {
+       checkIfClosed("updateBlob");
+       updateBlob(findColumnName(columnName), x);
+    }
+
      /**
       *
       * JDBC 4.0
@@ -4658,10 +4959,41 @@
              updateNull(columnIndex);
          } else {
              updateCharacterStreamInternal(
-                 columnIndex, x,length, "updateClob");
+                 columnIndex, x, false, length, "updateClob");
          }
      }
 
+    /**
+     * Updates the designated column using the given <code>Reader</code>
+     * object.
+     *
+     * The data will be read from the stream as needed until end-of-stream is
+     * reached. The JDBC driver will do any necessary conversion from
+     * <code>UNICODE</code> to the database char format.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the <code>updateRow</code> or <code>insertRow</code>
+     * methods are called to update the database.
+     *
+     * @param columnIndex the first column is 1, the second is 2, ...
+     * @param x an object that contains the data to set the parameter
+     *     value to
+     * @throws SQLException if the columnIndex is not valid; if a database
+     *     access error occurs; the result set concurrency is
+     *     <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *     result set
+     */
+    public void updateClob(int columnIndex, Reader x)
+           throws SQLException {
+        checksBeforeUpdateXXX("updateClob", columnIndex);
+        int colType = getColumnType(columnIndex);
+        if (colType != Types.CLOB) {
+            throw dataTypeConversion(columnIndex, "java.sql.Clob");
+        }
+        updateCharacterStreamInternal(columnIndex, x, true, -1, "updateClob");
+    }
+
      /**
       *
       * JDBC 4.0
@@ -4684,5 +5016,33 @@
          checkIfClosed("updateClob");
          updateClob(findColumnName(columnName),x,length);
      }
-}
 
+    /**
+     * Updates the designated column using the given <code>Reader</code>
+     * object.
+     *
+     * The data will be read from the stream as needed until end-of-stream is
+     * reached. The JDBC driver will do any necessary conversion from
+     * <code>UNICODE</code> to the database char format.
+     *
+     * The updater methods are used to update column values in the current row
+     * or the insert row. The updater methods do not update the underlying
+     * database; instead the <code>updateRow</code> or <code>insertRow</code>
+     * methods are called to update the database.
+     *
+     * @param columnName the label for the column specified with the SQL AS
+     *     clause. If the SQL AS clause was not specified, then the label is
+     *     the name of the column
+     * @param x an object that contains the data to set the parameter
+     *     value to
+     * @throws SQLException if the columnIndex is not valid; if a database
+     *     access error occurs; the result set concurrency is
+     *     <code>CONCUR_READ_ONLY</code> or this method is called on a closed
+     *     result set
+     */
+    public void updateClob(String columnName, Reader x)
+           throws SQLException {
+       checkIfClosed("updateClob");
+       updateClob(findColumnName(columnName), x);
+    }
+}

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=422995&r1=422994&r2=422995&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 Tue Jul 18 00:18:33 2006
@@ -34,6 +34,13 @@
  * object.
  */
 public class PreparedStatementTest extends BaseJDBCTestCase {
+
+    /** 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.
      */
@@ -106,6 +113,17 @@
     public static Test suite() {
         TestSuite suite = new TestSuite();
         suite.addTestSuite(PreparedStatementTest.class);
+        // Add methods temorarily disabled for DerbyNetClient
+        suite.addTest(new PreparedStatementTest(
+                    "embonlytmptestSetClobLengthless"));
+        suite.addTest(new PreparedStatementTest(
+                    "embonlytmptestSetBlobLengthless"));
+        suite.addTest(new PreparedStatementTest(
+                    "embonlytmptestSetCharacterStreamLengthless"));
+        suite.addTest(new PreparedStatementTest(
+                    "embonlytmptestSetAsciiStreamLengthless"));
+        suite.addTest(new PreparedStatementTest(
+                    "embonlytmptestSetBinaryStreamLengthless"));
         suite.addTest(SetObjectUnsupportedTest.suite(false));
         return suite;
     }
@@ -165,6 +183,26 @@
         }
     }
     
+    public void testSetNCharacterStreamLengthlessNotImplemented()
+            throws SQLException {
+        try {
+            ps.setNCharacterStream(1, new StringReader("A string"));
+            fail("setNCharacterStream(int,Reader) should not be implemented");
+        } catch (SQLFeatureNotSupportedException sfnse) {
+            // Do nothing, this is expected behavior.
+        }
+    }
+
+    public void testSetNClobLengthlessNotImplemented()
+            throws SQLException {
+        try {
+            ps.setNClob(1, new StringReader("A string"));
+            fail("setNClob(int,Reader) should not be implemented");
+        } catch (SQLFeatureNotSupportedException sfnse) {
+            // Do nothing, this is expected behaviour.
+        }
+    }
+
     /**
      * Tests the setNClob method of the PreparedStatement interface
      *
@@ -312,17 +350,13 @@
      * @throws SQLException if a failure occurs during the call to setClob
      *
      */
-    public void testSetClob() throws SQLException {
+    public void testSetClob()
+            throws IOException, SQLException {
         //insert default values into the table
         
         String str = "Test data for the Clob object";
         StringReader is = new StringReader("Test data for the Clob object");
-        
-        try {
-            is.reset();
-        } catch (IOException ioe) {
-            fail("Failed to reset blob input stream: " + ioe.getMessage());
-        }
+        is.reset();
         
         PreparedStatement ps_sc = conn.prepareStatement("insert into ClobTestTable values(?,?)");
         
@@ -334,12 +368,12 @@
         //Now query to retrieve the Clob
         ResultSet rs = s.executeQuery("select * from ClobTestTable where sno = 1");
         rs.next();
-        Clob clobToBeInerted = rs.getClob(2);
+        Clob clobToBeInserted = rs.getClob(2);
         rs.close();
         
         //Now use the setClob method
         ps_sc.setInt(1,2);
-        ps_sc.setClob(2,clobToBeInerted);
+        ps_sc.setClob(2,clobToBeInserted);
         ps_sc.execute();
         
         ps_sc.close();
@@ -349,46 +383,49 @@
         rs.next();
         Clob clobRetrieved = rs.getClob(2);
         
-        if(!equalClob(clobToBeInerted,clobRetrieved)) 
-            fail("Clob not inserted properly using setClob");
+        assertEquals(clobToBeInserted,clobRetrieved);
     }
-    
-    /*
+
+    /**
+     * Insert <code>Clob</code> without specifying length and read it back
+     * for verification.
      *
-     * Compares the two clobs supplied to se if they are similar
-     * returns true if they are similar and false otherwise.
-     * 
-     * @param clob1 Clob to be compared
-     * @param clob1 Clob to be compared
-     * @return true if they are equal
-     *
-     */
-    boolean equalClob(Clob clob1,Clob clob2) {
-        int c1,c2;
-        InputStream is1=null,is2=null;
-        try {
-            is1 = clob1.getAsciiStream();
-            is2 = clob2.getAsciiStream();
-            if(clob1.length()!=clob2.length())
-                return false;
-        } catch(SQLException sqle){
-            sqle.printStackTrace();
-        }
-        try {
-            for(long i=0;i<clob1.length();i++) {
-                c1=is1.read();
-                c2=is2.read();
-                if(c1!=c2)
-                    return false;
-            }
-        } catch(IOException e) {
-            e.printStackTrace();
-        } catch(SQLException e) {
-            e.printStackTrace();
-        }
-        return true;
+     * Beacuse we don't yet support <code>Connection.createClob</code> in the
+     * client driver, we must first insert data into the database and read back
+     * a <code>Clob</code> object. This object is then inserted into the
+     * database again.
+     */
+    public void embonlytmptestSetClobLengthless()
+            throws IOException, SQLException {
+        // Insert test data.
+        String testString = "Test string for setCharacterStream\u1A00";
+        Reader reader = new StringReader(testString);
+        PreparedStatement psChar = conn.prepareStatement(
+                "insert into ClobTestTable values (?,?)");
+        psChar.setInt(1, 1);
+        psChar.setCharacterStream(2, reader);
+        psChar.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");
+        assertTrue("No results retrieved", rs.next());
+        Clob insertClob = rs.getClob(1);
+        psChar.setInt(1, 2);
+        psChar.setClob(2, insertClob);
+        psChar.execute();
+
+        // Read back test data from database.
+        rs = s.executeQuery(
+                "select clobCol from ClobTestTable where sno = 2");
+        assertTrue("No results retrieved", rs.next());
+        Clob clobRetrieved = rs.getClob(1);
+
+        // Verify test data.
+        assertEquals(insertClob, clobRetrieved);
     }
-    
+
     /**
      *
      * Test the setBlob() method
@@ -396,37 +433,29 @@
      * @throws SQLException if a failure occurs during the call to setBlob
      *
      */
-    public void testSetBlob() throws SQLException {
+    public void testSetBlob()
+            throws IOException, SQLException {
         //insert default values into the table
         
-        byte[] bytes = new byte[] {
-            0x65, 0x66, 0x67, 0x68, 0x69,
-            0x69, 0x68, 0x67, 0x66, 0x65
-        };
-        InputStream is = new java.io.ByteArrayInputStream(bytes);
-        
-        try {
-            is.reset();
-        } catch (IOException ioe) {
-            fail("Failed to reset blob input stream: " + ioe.getMessage());
-        }
+        InputStream is = new java.io.ByteArrayInputStream(BYTES);
+        is.reset();
         
         PreparedStatement ps_sb = conn.prepareStatement("insert into BlobTestTable values(?,?)");
         
         //initially insert the data
         ps_sb.setInt(1,1);
-        ps_sb.setBlob(2,is,bytes.length);
+        ps_sb.setBlob(2,is,BYTES.length);
         ps_sb.executeUpdate();
         
         //Now query to retrieve the Blob
         ResultSet rs = s.executeQuery("select * from BlobTestTable where sno = 1");
         rs.next();
-        Blob blobToBeInerted = rs.getBlob(2);
+        Blob blobToBeInserted = rs.getBlob(2);
         rs.close();
         
         //Now use the setBlob method
         ps_sb.setInt(1,2);
-        ps_sb.setBlob(2,blobToBeInerted);
+        ps_sb.setBlob(2,blobToBeInserted);
         ps_sb.execute();
         
         ps_sb.close();
@@ -436,45 +465,48 @@
         rs.next();
         Blob blobRetrieved = rs.getBlob(2);
         
-        if(!equalBlob(blobToBeInerted,blobRetrieved)) 
-            fail("Blob not inserted properly using setBlob");
+        assertEquals(blobToBeInserted, blobRetrieved);
     }
     
-    /*
-     * Compares the two blobs supplied to se if they are similar
-     * returns true if they are similar and false otherwise.
-     *
-     * @param blob1 The first Blob that is passed as input
-     * @param blob2 The second Blob that is passed as input
+    /**
+     * Insert <code>Blob</code> without specifying length and read it back
+     * for verification.
      *
-     * @return true If the Blob values are equal
-     */
-    boolean equalBlob(Blob blob1,Blob blob2) {
-        int c1,c2;
-        InputStream is1=null,is2=null;
-        try {
-            is1 = blob1.getBinaryStream();
-            is2 = blob2.getBinaryStream();
-            if(blob1.length()!=blob2.length())
-                return false;
-        } catch(SQLException sqle){
-            sqle.printStackTrace();
-        }
-        try {
-            for(long i=0;i<blob1.length();i++) {
-                c1=is1.read();
-                c2=is2.read();
-                if(c1!=c2)
-                    return false;
-            }
-        } catch(IOException e) {
-            e.printStackTrace();
-        } catch(SQLException e) {
-            e.printStackTrace();
-        }
-        return true;
+     * Beacuse we don't yet support <code>Connection.createBlob</code> in the
+     * client driver, we must first insert data into the database and read back
+     * a <code>Blob</code> object. This object is then inserted into the
+     * database again.
+     */
+    public void embonlytmptestSetBlobLengthless()
+            throws IOException, SQLException {
+        // Insert test data.
+        InputStream is = new ByteArrayInputStream(BYTES);
+        PreparedStatement psByte = conn.prepareStatement(
+                "insert into BlobTestTable values (?,?)");
+        psByte.setInt(1, 1);
+        psByte.setBinaryStream(2, is);
+        psByte.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");
+        assertTrue("No results retrieved", rs.next());
+        Blob insertBlob = rs.getBlob(1);
+        psByte.setInt(1, 2);
+        psByte.setBlob(2, insertBlob);
+        psByte.execute();
+
+        // Read back test data from database.
+        rs = s.executeQuery(
+                "select blobCol from BlobTestTable where sno = 2");
+        assertTrue("No results retrieved", rs.next());
+        Blob blobRetrieved = rs.getBlob(1);
+
+        // Verify test data.
+        assertEquals(insertBlob, blobRetrieved);
     }
-    
+
     //-------------------------------------------------
     //Test the methods used to test poolable statements
     
@@ -570,7 +602,30 @@
         assertEquals("Error in inserting data into the Clob object",str,str_out);
         ps_sc.close();
     }
-    
+
+    public void embonlytmptestSetCharacterStreamLengthless()
+            throws IOException, SQLException {
+        // Insert test data.
+        String testString = "Test string for setCharacterStream\u1A00";
+        Reader reader = new StringReader(testString);
+        PreparedStatement psChar = conn.prepareStatement(
+                "insert into ClobTestTable values (?,?)");
+        psChar.setInt(1, 1);
+        psChar.setCharacterStream(2, reader);
+        psChar.execute();
+        reader.close();
+
+        // Read back test data from database.
+        ResultSet rs = s.executeQuery(
+                "select clobCol from ClobTestTable where sno = 1");
+        assertTrue("No results retrieved", rs.next());
+        Clob clobRetrieved = rs.getClob(1);
+
+        // Verify test data.
+        assertEquals("Mismatch test data in/out", testString,
+                     clobRetrieved.getSubString(1, testString.length()));
+    }
+
      /**
       *
       * Tests the PreparedStatement interface method setAsciiStream
@@ -582,14 +637,9 @@
     public void testSetAsciiStream() throws Exception {
         //insert default values into the table
         
-        byte[] bytes = new byte[] {
-            0x65, 0x66, 0x67, 0x68, 0x69,
-            0x69, 0x68, 0x67, 0x66, 0x65
-        };
-        
         byte [] bytes1 = new byte[10];
         
-        InputStream is = new java.io.ByteArrayInputStream(bytes);
+        InputStream is = new java.io.ByteArrayInputStream(BYTES);
         
         is.reset();
         
@@ -597,7 +647,7 @@
         
         //initially insert the data
         ps_sb.setInt(1,1);
-        ps_sb.setAsciiStream(2,is,bytes.length);
+        ps_sb.setAsciiStream(2,is,BYTES.length);
         ps_sb.executeUpdate();
         
         //Now query to retrieve the Clob
@@ -612,12 +662,44 @@
         } catch(IOException ioe) {
             fail("IOException while reading the Clob from the database");
         }
-        for(int i=0;i<bytes.length;i++) {
-            assertEquals("Error in inserting data into the Clob",bytes[i],bytes1[i]);
+        for(int i=0;i<BYTES.length;i++) {
+            assertEquals("Error in inserting data into the Clob",BYTES[i],bytes1[i]);
         }
         ps_sb.close();
     }
-    
+
+    public void embonlytmptestSetAsciiStreamLengthless()
+            throws IOException, SQLException {
+        // Insert test data.
+        InputStream is = new ByteArrayInputStream(BYTES);
+        PreparedStatement psAscii = conn.prepareStatement(
+                "insert into ClobTestTable values (?,?)");
+        psAscii.setInt(1, 1);
+        psAscii.setAsciiStream(2, is);
+        psAscii.execute();
+        is.close();
+
+        // Read back test data from database.
+        ResultSet rs = s.executeQuery(
+                "select clobCol from ClobTestTable where sno = 1");
+        assertTrue("No results retrieved", rs.next());
+        Clob clobRetrieved = rs.getClob(1);
+
+        // Verify read back data.
+        byte[] dbBytes = new byte[10];
+        InputStream isRetrieved = clobRetrieved.getAsciiStream();
+        assertEquals("Unexpected number of bytes read", BYTES.length,
+                isRetrieved.read(dbBytes));
+        assertEquals("Stream should be exhausted", -1, isRetrieved.read());
+        for (int i=0; i < BYTES.length; i++) {
+            assertEquals("Byte mismatch in/out", BYTES[i], dbBytes[i]);
+        }
+
+        // Cleanup
+        isRetrieved.close();
+        psAscii.close();
+    }
+
     /**
      *
      * Tests the PreparedStatement interface method setBinaryStream
@@ -629,14 +711,9 @@
     public void testSetBinaryStream() throws Exception {
         //insert default values into the table
         
-        byte[] bytes = new byte[] {
-            0x65, 0x66, 0x67, 0x68, 0x69,
-            0x69, 0x68, 0x67, 0x66, 0x65
-        };
-        
         byte [] bytes1 = new byte[10];
         
-        InputStream is = new java.io.ByteArrayInputStream(bytes);
+        InputStream is = new java.io.ByteArrayInputStream(BYTES);
         
         is.reset();
         
@@ -644,7 +721,7 @@
         
         //initially insert the data
         ps_sb.setInt(1,1);
-        ps_sb.setBinaryStream(2,is,bytes.length);
+        ps_sb.setBinaryStream(2,is,BYTES.length);
         ps_sb.executeUpdate();
         
         //Now query to retrieve the Clob
@@ -660,9 +737,41 @@
             fail("IOException while reading the Clob from the database");
         }
         
-        for(int i=0;i<bytes.length;i++) {
-            assertEquals("Error in inserting data into the Blob",bytes[i],bytes1[i]);
+        for(int i=0;i<BYTES.length;i++) {
+            assertEquals("Error in inserting data into the Blob",BYTES[i],bytes1[i]);
         }
         ps_sb.close();
+    }
+
+    public void embonlytmptestSetBinaryStreamLengthless()
+            throws IOException, SQLException {
+        // Insert test data.
+        InputStream is = new ByteArrayInputStream(BYTES);
+        PreparedStatement psBinary = conn.prepareStatement(
+                "insert into BlobTestTable values (?,?)");
+        psBinary.setInt(1, 1);
+        psBinary.setBinaryStream(2, is);
+        psBinary.execute();
+        is.close();
+
+        // Read back test data from database.
+        ResultSet rs = s.executeQuery(
+                "select blobCol from BlobTestTable where sno = 1");
+        assertTrue("No results retrieved", rs.next());
+        Blob blobRetrieved = rs.getBlob(1);
+
+        // Verify read back data.
+        byte[] dbBytes = new byte[10];
+        InputStream isRetrieved = blobRetrieved.getBinaryStream();
+        assertEquals("Unexpected number of bytes read", BYTES.length,
+                isRetrieved.read(dbBytes));
+        assertEquals("Stream should be exhausted", -1, isRetrieved.read());
+        for (int i=0; i < BYTES.length; i++) {
+            assertEquals("Byte mismatch in/out", BYTES[i], dbBytes[i]);
+        }
+
+        // Cleanup
+        isRetrieved.close();
+        psBinary.close();
     }
 }