db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mi...@apache.org
Subject svn commit: r326307 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/reference/ engine/org/apache/derby/impl/jdbc/ engine/org/apache/derby/impl/store/raw/data/ engine/org/apache/derby/loc/ testing/org/apache/derbyTesting/functionTests/maste...
Date Wed, 19 Oct 2005 00:11:50 GMT
Author: mikem
Date: Tue Oct 18 17:11:30 2005
New Revision: 326307

URL: http://svn.apache.org/viewcvs?rev=326307&view=rev
Log:
committing fix for DERBY-500 on behalf of: Sunitha Kambhampati

 Background :
 In Derby, when a stream is set as a parameter value, the wrapper stream object used for character
data is ReaderToUTF8Stream
 and for binary data it is RawToBinaryFormatStream.Both these stream objects on read() return
data in a format that is used to store the respective datatype value. E.g in case of char,
the characters read from the user stream are converted using utf-8 derby specific encoding
and read calls return
 the data as expected by store layer. Beginning 2 bytes either have the utflen or has zeroes,
or if it is a long string, then the value is ended with the special marker 0xE0 , 0x00, 0x00.
For binary data, the stream data is prepended with 4 zeroes.

 Problem:
 once,the stream has been read fully and end of file reached, further read() returns a -1.
If a stream is re-read, it returns a -1 which is incorrect data. E.g.in the repro for DERBY-500,
the update statement has multiple rows that qualify and since the stream parameter is used;
the first row gets updated with the correct value and the stream is drained. For the subsequent
rows, the read from the stream parameter value returns -1 and thus is updated with incorrect
data.When retrieving the row back, the format of the fields is incorrect and thus the exception.
 __________
 This patch

 1. adds changes to RawToBinaryFormatStream and ReaderToUTF8Stream to throw an EOFException
if stream is re-read.
 If a stream value has been fully read and end of file reached, any further reads on the stream
object will result in an EOFException. This seems reasonable and more correct than using incorrect
values.
 Adds a new error message - 'Stream has already been read and end-of-file reached and cannot
be re-used.'

 2. changes to RememberBytesInputStream to keep track of the stream state and not call read
on the stream objects once eof is reached.

 3. Fix a bug in StoredPage.logColumn related to streams. In one particular scenario, column
was not being set to RememberBytesInputStream object and thus losing the data that would be
read from stream into RememberBytesInputStream.

 4. adds testcases to store/streamingColumn.java and lang/forbitdata.java


 Also note
 - This fix affects cases when a stream is re-used in which case an exception will be thrown.
 So code that reads the stream once and materializes it will not be affected. E.g. Currently
in case of char,varchar,long varchar, streams are materialized and this will work fine as
before. 


Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/SQLState.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/RawToBinaryFormatStream.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ReaderToUTF8Stream.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/RememberBytesInputStream.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/forbitdata.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/lang/forbitdata.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/SQLState.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/SQLState.java?rev=326307&r1=326306&r2=326307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/SQLState.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/reference/SQLState.java Tue Oct
18 17:11:30 2005
@@ -1361,6 +1361,7 @@
     String INVALID_JDBCTYPE = "XJ021.S";
     String SET_STREAM_FAILURE = "XJ022.S";
     String SET_STREAM_INEXACT_LENGTH_DATA = "XJ023.S";
+    String STREAM_EOF = "XJ085.S";
     String SET_UNICODE_INVALID_LENGTH = "XJ024.S";
     String NEGATIVE_STREAM_LENGTH = "XJ025.S";
     String NO_AUTO_COMMIT_ON = "XJ030.S";

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/RawToBinaryFormatStream.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/RawToBinaryFormatStream.java?rev=326307&r1=326306&r2=326307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/RawToBinaryFormatStream.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/RawToBinaryFormatStream.java
Tue Oct 18 17:11:30 2005
@@ -23,6 +23,7 @@
 import java.io.InputStream;
 import java.io.FilterInputStream;
 import java.io.IOException;
+import java.io.EOFException;
 
 import org.apache.derby.iapi.services.io.LimitInputStream;
 import org.apache.derby.iapi.services.i18n.MessageService;
@@ -32,10 +33,16 @@
 	Stream that takes a raw input stream and converts it
 	to the format of the binary types by prepending the
 	length of the value. In this case 0 is always written.
+    Note: This stream cannot be re-used. Once end of file is
+    reached, the next read call will throw an EOFException
 */
 class RawToBinaryFormatStream extends LimitInputStream {
 
 	private int dummyBytes = 4;
+    
+    // flag to indicate the stream has already been read
+    // and eof reached
+    private boolean eof = false;
 
 	/**
 		@param	in Application's raw binary stream passed into JDBC layer
@@ -50,9 +57,15 @@
 
 	/**
 		Read from the wrapped stream prepending the intial bytes if needed.
+        If stream has been read, and eof reached, in that case any subsequent
+        read will throw an EOFException
 	*/
 	public int read() throws IOException {
 
+        if ( eof )
+            throw new EOFException(MessageService.getTextMessage
+                        (SQLState.STREAM_EOF));
+        
 		if (dummyBytes != 0) {
 			dummyBytes--;
 			return 0;
@@ -73,8 +86,13 @@
 	*/
 	private void checkSufficientData() throws IOException
 	{
+        // if we reached here, then read call returned -1, and we 
+        // have already reached the end of stream, so set eof=true
+        // so that subsequent reads on this stream will return an 
+        // EOFException
+        eof = true;
 		if (!limitInPlace)
-			return;
+        return;
 
 		int remainingBytes = clearLimit();
 
@@ -99,8 +117,13 @@
 
 	/**
 		Read from the wrapped stream prepending the intial bytes if needed.
+        If stream has been read, and eof reached, in that case any subsequent
+        read will throw an EOFException
 	*/
 	public int read(byte b[], int off, int len) throws IOException {
+  
+        if ( eof )
+            throw new EOFException(MessageService.getTextMessage(SQLState.STREAM_EOF));
 
 		int dlen = dummyBytes;
 
@@ -124,7 +147,6 @@
 				return dlen;
 
 			checkSufficientData();
-
 			return realRead;
 		}
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ReaderToUTF8Stream.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ReaderToUTF8Stream.java?rev=326307&r1=326306&r2=326307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ReaderToUTF8Stream.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ReaderToUTF8Stream.java Tue
Oct 18 17:11:30 2005
@@ -69,8 +69,22 @@
         this.colWidth = length;
 	}
 
+    /**
+     * read from stream; characters converted to utf-8 derby specific encoding.
+     * If stream has been read, and eof reached, in that case any subsequent
+     * read will throw an EOFException
+     * @see java.io.InputStream#read()
+     */
 	public int read() throws IOException {
 
+        // when stream has been read and eof reached, stream is closed
+        // and buffer is set to null ( see close() method)
+        // since stream cannot be re-used, check if stream is closed and 
+        // if so throw an EOFException
+        if ( buffer == null)
+            throw new EOFException(MessageService.getTextMessage(SQLState.STREAM_EOF));
+
+        
 		// first read
 		if (blen < 0)
 			fillBuffer(2);
@@ -79,7 +93,14 @@
 		{
 			// reached end of buffer, read more?
 			if (eof)
-				return -1;
+            {
+               // we have reached the end of this stream
+               // cleanup here and return -1 indicating 
+               // eof of stream
+               close();
+               return -1;
+            }
+                
 
 			fillBuffer(0);
 		}
@@ -89,7 +110,16 @@
 	}
 
 	public int read(byte b[], int off, int len) throws IOException {
-		// first read
+        
+        // when stream has been read and eof reached, stream is closed
+        // and buffer is set to null ( see close() method)
+        // since stream cannot be re-used, check if stream is closed and 
+        // if so throw an EOFException
+        if ( buffer == null )
+            throw new EOFException(MessageService.getTextMessage
+                    (SQLState.STREAM_EOF));
+
+        // first read
 		if (blen < 0)
 			fillBuffer(2);
 
@@ -105,8 +135,17 @@
 			{
 				if (eof)
 				{
-                    // return actual number of bytes read into b[]
-					return readCount > 0 ? readCount : -1;
+                    if (readCount > 0)
+                    {
+                        return readCount;
+                    }
+                    else
+                    {
+                        // we have reached the eof, so close the stream
+                        close();
+                        return -1;  
+                    }
+                    
 				}
 				fillBuffer(0);
 				continue;
@@ -122,7 +161,6 @@
             off += copyBytes;
 
 		}
-
 		return readCount;
 	}
 
@@ -234,6 +272,7 @@
 
 			buffer[0] = (byte) ((utflen >>> 8) & 0xFF);
 			buffer[1] = (byte) ((utflen >>> 0) & 0xFF);
+
 		}
 		else
 		{
@@ -243,10 +282,16 @@
 		}
 	}
 
+    /**
+     * return resources 
+     */
 	public void close() throws IOException
 	{
-		buffer = null;
-		reader.close();
+        // since stream has been read and eof reached, return buffer back to 
+        // the vm.
+        // Instead of using another variable to indicate stream is closed
+        // a check on (buffer==null) is used instead. 
+        buffer = null;
 	}
 
     /**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/RememberBytesInputStream.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/RememberBytesInputStream.java?rev=326307&r1=326306&r2=326307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/RememberBytesInputStream.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/RememberBytesInputStream.java
Tue Oct 18 17:11:30 2005
@@ -44,6 +44,12 @@
 {
 	ByteHolder bh;
 	boolean recording = true;
+    
+    // In case of streams (e.g ReaderToUTF8Stream,
+    // RawToBinaryFormatStream) that cannot be re-used
+    // a read on a closed stream will throw an EOFException
+    // hence keep track if the stream is closed or not
+    boolean streamClosed = false;
 	
 	/**
 	  Construct a RememberBytesInputStream.
@@ -58,6 +64,7 @@
             SanityManager.ASSERT(bh.writingMode());
 
 		this.bh = bh;
+
 	}
 	
 	/**
@@ -69,10 +76,19 @@
 		if (SanityManager.DEBUG)
 			SanityManager.ASSERT(recording,
 								 "Must be in record mode to perform a read.");
-		int value = super.read();
-		if (value != -1)
-			bh.write(value);
-		return value;
+		
+        int value = -1;
+        
+        if ( !streamClosed )
+        {
+            value = super.read();
+            if ( value != -1 )
+                bh.write(value);
+            else
+                streamClosed =true;
+        }
+		
+        return value;
 	}
 
 	/**
@@ -84,22 +100,46 @@
 		if (SanityManager.DEBUG)
 			SanityManager.ASSERT(recording,
 								 "Must be in record mode to perform a read.");
-		if ((len + off) > b.length)
-			len = b.length - off;
-		len = super.read(b,off,len);
-		if (len != -1)
-			bh.write(b,off,len);
-		return len;
+		
+        if ( !streamClosed ) {
+            if ((len + off) > b.length)
+                len = b.length - off;
+
+            len = super.read(b, off, len);
+            if (len > 0 )
+                bh.write(b, off, len);
+            else
+                streamClosed = true;
+        } else {
+            return -1;
+        }
+
+        return len;
 	}
 
 	/**
 	  read len bytes from the input stream, and store it in the byte holder.
 
+      Note, fillBuf does not return negative values, if there are no 
+      bytes to store in the byteholder, it will return 0
 	  @exception IOException thrown on an io error spooling rememberd bytes
 	             to backing storage.
 	  */
 	public long fillBuf(int len) throws IOException{
-		return bh.write(this.in, len);
+        
+        long val = 0;
+
+        if ( !streamClosed )
+        {
+            val = bh.write(this.in, len);
+            
+            // if bh.write returns less than len, then the stream
+            // has reached end of stream. See logic in MemByteHolder.write
+            if ( val < len )
+                streamClosed=true;
+        }       
+
+        return val;
 	}
 
 	/**
@@ -160,6 +200,7 @@
 	 */
 	public void setInput(InputStream in) {
 		this.in = in;
+        streamClosed = false;
 	}
 
 	/**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java?rev=326307&r1=326306&r2=326307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java Tue
Oct 18 17:11:30 2005
@@ -6154,6 +6154,13 @@
                 // into the RememberBytesInputStream.
                 if (row[arrayPosition] instanceof StreamStorable)
                     ((StreamStorable)row[arrayPosition]).setStream(bufferedIn);
+                
+                // set column to the RememberBytesInputStream so that 
+                // all future access to this column will be able to get 
+                // at bytes that have been already read. This assignment
+                // is needed to ensure that if long column exception is 
+                // thrown, the column is set correctly
+                column = bufferedIn;
 			}
 
 			// read the buffer by reading the max we can read.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties?rev=326307&r1=326306&r2=326307&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages_en.properties Tue Oct 18
17:11:30 2005
@@ -1097,7 +1097,7 @@
 XJ082.U=BLOB/CLOB values are not allowed as method parameters or receiver.
 XJ083.U=''{0}'' not allowed because the ResultSet is not an updatable ResultSet. 
 XJ084.U=Column does not correspond to a column in the base table. Cannot issue ''{0}'' on
this column.
-
+XJ085.S=Stream has already been read and end-of-file reached and cannot be re-used.
 0A000.S=Feature not implemented: {0}.
 
 XJ004.C=Database ''{0}'' not found.

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/forbitdata.out
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/forbitdata.out?rev=326307&r1=326306&r2=326307&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/forbitdata.out
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/forbitdata.out
Tue Oct 18 17:11:30 2005
@@ -599,32 +599,62 @@
 START testEncodedLengths
  EL byte[] 10 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL stream 10 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL byte[] 10 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL stream 10 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL byte[] 30 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL stream 30 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL byte[] 30 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL stream 30 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL byte[] 31 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL stream 31 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL byte[] 31 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL stream 31 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL byte[] 32 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL stream 32 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL byte[] 32 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL stream 32 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL byte[] 1345 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL stream 1345 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL byte[] 1345 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL stream 1345 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL byte[] 23456 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL stream 23456 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL byte[] 23456 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL stream 23456 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL byte[] 32672 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
  EL stream 32672 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL byte[] 32672 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL stream 32672 C1 OK DATA OK C2 OK DATA OK C3 OK DATA OK
+ EL byte[] 32700 C1 NULL C2 OK DATA OK C3 OK DATA OK
+ EL stream 32700 C1 NULL C2 OK DATA OK C3 OK DATA OK
  EL byte[] 32700 C1 NULL C2 OK DATA OK C3 OK DATA OK
  EL stream 32700 C1 NULL C2 OK DATA OK C3 OK DATA OK
  EL byte[] 32767 C1 NULL C2 NULL C3 OK DATA OK
  EL stream 32767 C1 NULL C2 NULL C3 OK DATA OK
+ EL byte[] 32767 C1 NULL C2 NULL C3 OK DATA OK
+ EL stream 32767 C1 NULL C2 NULL C3 OK DATA OK
  EL byte[] 32768 C1 NULL C2 NULL C3 OK DATA OK
  EL stream 32768 C1 NULL C2 NULL C3 OK DATA OK
+ EL byte[] 32768 C1 NULL C2 NULL C3 OK DATA OK
+ EL stream 32768 C1 NULL C2 NULL C3 OK DATA OK
+ EL byte[] 32769 C1 NULL C2 NULL C3 OK DATA OK
+ EL stream 32769 C1 NULL C2 NULL C3 OK DATA OK
  EL byte[] 32769 C1 NULL C2 NULL C3 OK DATA OK
  EL stream 32769 C1 NULL C2 NULL C3 OK DATA OK
  EL byte[] 65535 C1 NULL C2 NULL C3 OK DATA OK
  EL stream 65535 C1 NULL C2 NULL C3 OK DATA OK
+ EL byte[] 65535 C1 NULL C2 NULL C3 OK DATA OK
+ EL stream 65535 C1 NULL C2 NULL C3 OK DATA OK
+ EL byte[] 65536 C1 NULL C2 NULL C3 OK DATA OK
+ EL stream 65536 C1 NULL C2 NULL C3 OK DATA OK
  EL byte[] 65536 C1 NULL C2 NULL C3 OK DATA OK
  EL stream 65536 C1 NULL C2 NULL C3 OK DATA OK
  EL byte[] 65537 C1 NULL C2 NULL C3 OK DATA OK
  EL stream 65537 C1 NULL C2 NULL C3 OK DATA OK
+ EL byte[] 65537 C1 NULL C2 NULL C3 OK DATA OK
+ EL stream 65537 C1 NULL C2 NULL C3 OK DATA OK
+ EL byte[] 115882 C1 NULL C2 NULL C3 OK DATA OK
+ EL stream 115882 C1 NULL C2 NULL C3 OK DATA OK
  EL byte[] 115882 C1 NULL C2 NULL C3 OK DATA OK
  EL stream 115882 C1 NULL C2 NULL C3 OK DATA OK
 END testEncodedLengths

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out?rev=326307&r1=326306&r2=326307&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
Tue Oct 18 17:11:30 2005
@@ -127,4 +127,39 @@
 ===> verified length 32672
 ===> verified length 32672
 Test 14 - clob truncation tests end in here
+======================================
+START  DERBY-500 TEST 
+Inserting rows 
+Rows inserted =10
+Rows selected =10
+Update qualifies many rows + streams
+EXPECTED EXCEPTION - streams cannot be re-used
+EXPECTED SQL Exception: (XSDA4) An unexpected exception was thrown
+EXPECTED SQL Exception: (XJ001) Java exception: 'Stream has already been read and end-of-file
reached and cannot be re-used.: java.io.EOFException'.
+Rows selected =10
+DERBY500 #2 Rows updated  =1
+Rows selected =1
+DERBY500 #3 Rows deleted =10
+Rows selected =0
+DERBY500 #4 Rows deleted  =0
+Rows selected =0
+EXPECTED EXCEPTION - streams cannot be re-used
+EXPECTED SQL Exception: (XSDA4) An unexpected exception was thrown
+EXPECTED SQL Exception: (XJ001) Java exception: 'Stream has already been read and end-of-file
reached and cannot be re-used.: java.io.EOFException'.
+END  DERBY-500 TEST 
+======================================
+======================================
+START  DERBY-500 TEST for varchar 
+Rows inserted =10
+Rows selected =10
+DERBY500 for varchar #1 Rows updated  =11
+Rows selected =10
+DERBY500 for varchar #2 Rows updated  =1
+Rows selected =1
+DERBY500 for varchar #3 Rows deleted =11
+Rows selected =0
+DERBY500 for varchar #4 Rows deleted  =0
+Rows selected =0
+END  DERBY-500 TEST  for varchar
+======================================
 Test streamingColumn finished

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/forbitdata.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/forbitdata.java?rev=326307&r1=326306&r2=326307&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/forbitdata.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/forbitdata.java
Tue Oct 18 17:11:30 2005
@@ -717,44 +717,7 @@
 		psi.executeUpdate();
 		conn.commit();
 
-		ResultSet rs = pss.executeQuery();
-		while (rs.next())
-		{
-			System.out.print(" EL byte[] " + length);
-			byte[] v = rs.getBytes(1);
-			if (v != null) {
-				System.out.print(" C1 " + ((v.length == length) ? "OK" : ("FAIL <" + v.length + ">")));
-				System.out.print(" DATA " + ((v[off] == 0x23) ? "OK" : ("FAIL " + off)));
-			}
-			else
-				System.out.print(" C1 NULL");
-
-			v = rs.getBytes(2);
-			if (v != null) {
-				System.out.print(" C2 " + ((v.length == length) ? "OK" : ("FAIL <" + v.length + ">")));
-				System.out.print(" DATA " + ((v[off] == 0x23) ? "OK" : ("FAIL " + off)));
-			}
-			else
-				System.out.print(" C2 NULL");
-			InputStream c3 = rs.getBinaryStream(3);
-			checkEncodedLengthValue("C3", c3, length, off);
-
-			System.out.println("");
-		}
-		rs.close();
-
-		rs = pss.executeQuery();
-		while (rs.next())
-		{
-			System.out.print(" EL stream " + length);
-
-			checkEncodedLengthValue("C1", rs.getBinaryStream(1), length, off);
-			checkEncodedLengthValue("C2", rs.getBinaryStream(2), length, off);
-			checkEncodedLengthValue("C3", rs.getBinaryStream(3), length, off);
-
-			System.out.println("");
-		}
-		rs.close();
+		selectData(pss,data,off,length);
 
 		conn.commit();
 
@@ -762,21 +725,68 @@
 		conn.commit();
 
 
+        // Set values using stream and then verify that select is successful
 		psi.setBinaryStream(1, (length <= 32672) ? new java.io.ByteArrayInputStream(data) :
null, length);
 		psi.setBinaryStream(2, (length <= 32700) ? new java.io.ByteArrayInputStream(data) :
null, length);
 		psi.setBinaryStream(3, new java.io.ByteArrayInputStream(data), length); // BLOB column
 		psi.executeUpdate();
 		conn.commit();
 
-		psd.executeUpdate();
+		selectData(pss,data,off,length);
 
+        conn.commit();
 
 
+		psd.executeUpdate();
 		conn.commit();
 
 
 	}
 
+    private static void selectData(PreparedStatement pss,byte[] data,int off,int length)
+        throws SQLException,IOException
+    {
+        
+        ResultSet rs = pss.executeQuery();
+        while (rs.next())
+        {
+            System.out.print(" EL byte[] " + length);
+            byte[] v = rs.getBytes(1);
+            if (v != null) {
+                System.out.print(" C1 " + ((v.length == length) ? "OK" : ("FAIL <" + v.length
+ ">")));
+                System.out.print(" DATA " + ((v[off] == 0x23) ? "OK" : ("FAIL " + off)));
+            }
+            else
+                System.out.print(" C1 NULL");
+
+            v = rs.getBytes(2);
+            if (v != null) {
+                System.out.print(" C2 " + ((v.length == length) ? "OK" : ("FAIL <" + v.length
+ ">")));
+                System.out.print(" DATA " + ((v[off] == 0x23) ? "OK" : ("FAIL " + off)));
+            }
+            else
+                System.out.print(" C2 NULL");
+            InputStream c3 = rs.getBinaryStream(3);
+            checkEncodedLengthValue("C3", c3, length, off);
+
+            System.out.println("");
+        }
+        rs.close();
+
+        rs = pss.executeQuery();
+        while (rs.next())
+        {
+            System.out.print(" EL stream " + length);
+
+            checkEncodedLengthValue("C1", rs.getBinaryStream(1), length, off);
+            checkEncodedLengthValue("C2", rs.getBinaryStream(2), length, off);
+            checkEncodedLengthValue("C3", rs.getBinaryStream(3), length, off);
+
+            System.out.println("");
+        }
+        rs.close();
+
+    }
 	private static void checkEncodedLengthValue(String col, InputStream is, int length, int
off) throws IOException {
 
 		if (is == null) {

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn.java?rev=326307&r1=326306&r2=326307&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn.java
Tue Oct 18 17:11:30 2005
@@ -97,7 +97,7 @@
 			streamTest5(conn, 5000);
         //  This test fails when running w/ derby.language.logStatementText=true
         //  see DERBY-595 
-		//	streamTest5(conn, 100000);
+			//streamTest5(conn, 100000);
 
 			streamTest6(conn, 5000);
 			streamTest7(conn);
@@ -127,6 +127,19 @@
             // exception is thrown
             // This test is similar to streamTest12.
             streamTest14(conn);
+            
+            
+            // Derby500
+            // user supplied stream parameter values are not re-used
+            derby500Test(conn);
+
+            // currently in case of char,varchar,long varchar types
+            // stream paramter value is materialized the first time around
+            // and used for executions. Hence verify that the fix to 
+            // DERBY-500 did not change the behavior for char,varchar
+            // and long varchar types when using streams.
+            derby500_verifyVarcharStreams(conn);
+            
 			// turn autocommit on because in JCC, java.sql.Connection.close() can not be
 			// requested while a transaction is in progress on the connection.
 			// If autocommit is off in JCC, the transaction remains active, 
@@ -1222,6 +1235,438 @@
         System.out.println("Test 14 - clob truncation tests end in here");
     }
 
+
+    /**
+     * Streams are not re-used. This test tests the fix for 
+     * DERBY-500. If an update statement has multiple rows that
+     * is affected, and one of the parameter values is a stream,
+     * the update will fail because streams are not re-used.
+     * @param conn database connection
+     */
+    private static void derby500Test(Connection conn) {
+
+        Statement stmt;
+
+        System.out.println("======================================");
+        System.out.println("START  DERBY-500 TEST ");
+
+        try {
+            stmt = conn.createStatement();
+            conn.setAutoCommit(false);
+            stmt.execute("CREATE TABLE t1 (" + "id INTEGER NOT NULL,"
+                    + "mname VARCHAR( 254 ) NOT NULL," + "mvalue INT NOT NULL,"
+                    + "bytedata BLOB NOT NULL," + "chardata CLOB NOT NULL,"
+                    + "PRIMARY KEY ( id ))");
+
+            PreparedStatement ps = conn
+                    .prepareStatement("insert into t1 values (?,?,?,?,?)");
+
+            // insert 10 rows.
+            int rowCount = 0;
+            // use blob and clob values
+            int len = 10000;
+            byte buf[] = new byte[len];
+            char cbuf[] = new char[len];
+            char orig =  'c';
+            for (int i = 0; i < len; i++) {
+                buf[i] = (byte)orig;
+                cbuf[i] = orig;
+            }
+            int randomOffset = 9998;
+            buf[randomOffset] = (byte) 'e';
+            cbuf[randomOffset] = 'e';
+            System.out.println("Inserting rows ");
+            for (int i = 0; i < 10; i++) {
+                ps.setInt(1, i);
+                ps.setString(2, "mname" + i);
+                ps.setInt(3, 0);
+                ps.setBinaryStream(4, new ByteArrayInputStream(buf), len);
+                ps.setAsciiStream(5, new ByteArrayInputStream(buf), len);
+                rowCount += ps.executeUpdate();
+            }
+            conn.commit();
+            System.out.println("Rows inserted =" + rowCount);
+
+            
+            //conn.commit();
+            PreparedStatement pss = conn
+                    .prepareStatement(" select chardata,bytedata from t1 where id = ?");
+            verifyDerby500Test(pss, buf, cbuf,0, 10, true);
+            
+            // do the update, update must qualify more than 1 row and update will fail
+            // as currently we dont allow stream values to be re-used
+            PreparedStatement psu = conn
+                    .prepareStatement("update t1 set bytedata = ? "
+                            + ", chardata = ? where mvalue = ?  ");
+
+            buf[randomOffset + 1] = (byte) 'u';
+            cbuf[randomOffset +1] = 'u';
+            rowCount = 0;
+            System.out.println("Update qualifies many rows + streams");
+
+            try {
+                psu.setBinaryStream(1, new ByteArrayInputStream(buf), len);
+                psu.setCharacterStream(2, new CharArrayReader(cbuf), len);
+                psu.setInt(3, 0);
+                rowCount += psu.executeUpdate();
+                System.out.println("DERBY500 #1 Rows updated  ="
+                        + rowCount);
+
+            } catch (SQLException sqle) {
+                System.out
+                        .println("EXPECTED EXCEPTION - streams cannot be re-used");
+                expectedException(sqle);
+                conn.rollback();
+            }
+            
+            //verify data
+            //set back buffer value to what was inserted.
+            buf[randomOffset + 1] = (byte)orig;
+            cbuf[randomOffset + 1] = orig;
+            
+            verifyDerby500Test(pss, buf,cbuf, 0, 10,true);
+
+            PreparedStatement psu2 = conn
+                    .prepareStatement("update t1 set bytedata = ? "
+                            + ", chardata = ? where id = ?  ");
+
+            buf[randomOffset + 1] = (byte) 'u';
+            cbuf[randomOffset + 1] = 'u';
+            
+            rowCount = 0;
+            try {
+                psu2.setBinaryStream(1, new ByteArrayInputStream(buf), len);
+                psu2.setAsciiStream(2, new ByteArrayInputStream(buf), len);
+                psu2.setInt(3, 0);
+                rowCount += psu2.executeUpdate();
+                System.out.println("DERBY500 #2 Rows updated  ="
+                        + rowCount);
+
+            } catch (SQLException sqle) {
+                System.out
+                        .println("UNEXPECTED EXCEPTION - update should have actually gone
through");
+                dumpSQLExceptions(sqle);
+            }
+            conn.commit();
+            verifyDerby500Test(pss, buf,cbuf, 0, 1,true);
+            
+            // delete
+            // as currently we dont allow stream values to be re-used
+            PreparedStatement psd = conn
+                    .prepareStatement("delete from t1 where mvalue = ?");
+
+            rowCount = 0;
+            try {
+                psd.setInt(1, 0);
+                rowCount += psd.executeUpdate();
+                rowCount += psd.executeUpdate();
+                System.out.println("DERBY500 #3 Rows deleted ="
+                        + rowCount);
+
+            } catch (SQLException sqle) {
+                System.out
+                .println("UNEXPECTED EXCEPTION - delete should have actually gone through");
+                dumpSQLExceptions(sqle);
+            }
+
+            conn.commit();
+            //verify data
+           
+            verifyDerby500Test(pss, buf,cbuf, 0, 10, true);
+
+            PreparedStatement psd2 = conn
+                    .prepareStatement("delete from t1 where id = ?");
+            
+            rowCount = 0;
+            try {
+                psd2.setInt(1, 0);
+                rowCount += psd2.executeUpdate();
+                System.out.println("DERBY500 #4 Rows deleted  ="
+                        + rowCount);
+
+            } catch (SQLException sqle) {
+                System.out
+                        .println("UNEXPECTED EXCEPTION - delete should have actually gone
through");
+                dumpSQLExceptions(sqle);
+            }
+            conn.commit();
+            verifyDerby500Test(pss, buf,cbuf, 1, 2,true);
+
+            try
+            {
+                ps.setInt(1,11);
+                rowCount += ps.executeUpdate();
+                System.out.println("Rows inserted = "+ rowCount);
+            } catch (SQLException sqle) {
+                System.out
+                        .println("EXPECTED EXCEPTION - streams cannot be re-used");
+                expectedException(sqle);
+                conn.rollback();
+            }
+
+            stmt.execute("drop table t1");
+            conn.commit();
+            stmt.close();
+            pss.close();
+            psu2.close();
+            psu.close();
+            psd.close();
+            psd2.close();
+            System.out.println("END  DERBY-500 TEST ");
+            System.out.println("======================================");
+
+        } catch (SQLException sqle) {
+            dumpSQLExceptions(sqle);
+        } catch (Exception e) {
+            System.out.println("DERBY-500 TEST FAILED!");
+            e.printStackTrace();
+        }
+
+    }
+
+    /**
+     * Test that DERBY500 fix did not change the behavior for varchar,
+     * char, long varchar types when stream api is used. 
+     * Currently, for char,varchar and long varchar - the stream is 
+     * read once and materialized, hence the materialized stream value
+     * will/can be used for multiple executions of the prepared statement  
+     * @param conn database connection
+     */
+    private static void derby500_verifyVarcharStreams(Connection conn) {
+
+        Statement stmt;
+
+        System.out.println("======================================");
+        System.out.println("START  DERBY-500 TEST for varchar ");
+
+        try {
+            stmt = conn.createStatement();
+            stmt.execute("CREATE TABLE t1 (" + "id INTEGER NOT NULL,"
+                    + "mname VARCHAR( 254 ) NOT NULL," + "mvalue INT NOT NULL,"
+                    + "vc varchar(32500)," + "lvc long varchar NOT NULL,"
+                    + "PRIMARY KEY ( id ))");
+
+            PreparedStatement ps = conn
+                    .prepareStatement("insert into t1 values (?,?,?,?,?)");
+
+            // insert 10 rows.
+            int rowCount = 0;
+            // use blob and clob values
+            int len = 10000;
+            byte buf[] = new byte[len];
+            char cbuf[] = new char[len];
+            char orig =  'c';
+            for (int i = 0; i < len; i++) {
+                buf[i] = (byte)orig;
+                cbuf[i] = orig;
+            }
+            int randomOffset = 9998;
+            buf[randomOffset] = (byte)'e';
+            cbuf[randomOffset] = 'e';
+            for (int i = 0; i < 10; i++) {
+                ps.setInt(1, i);
+                ps.setString(2, "mname" + i);
+                ps.setInt(3, 0);
+                ps.setCharacterStream(4, new CharArrayReader(cbuf), len);
+                ps.setAsciiStream(5, new ByteArrayInputStream(buf), len);
+                rowCount += ps.executeUpdate();
+            }
+            conn.commit();
+            System.out.println("Rows inserted =" + rowCount);
+
+            try
+            {
+                ps.setInt(1,11);
+                rowCount += ps.executeUpdate();
+            } catch (SQLException sqle) {
+                System.out.println("UNEXPECTED EXCEPTION - streams cannot be "+
+                   "re-used but in case of varchar, stream is materialized the"+
+                   " first time around. So multiple executions using streams should "+
+                   " work fine. ");
+                dumpSQLExceptions(sqle);
+            }
+            
+            PreparedStatement pss = conn
+                    .prepareStatement(" select lvc,vc from t1 where id = ?");
+            verifyDerby500Test(pss, buf, cbuf,0, 10,false);
+            
+            // do the update, update must qualify more than 1 row and update will
+            // pass for char,varchar,long varchar columns.
+            PreparedStatement psu = conn
+                    .prepareStatement("update t1 set vc = ? "
+                            + ", lvc = ? where mvalue = ?  ");
+
+            buf[randomOffset +1] = (byte)'u';
+            cbuf[randomOffset +1] = 'u';
+            rowCount = 0;
+            try {
+                psu.setAsciiStream(1, new ByteArrayInputStream(buf), len);
+                psu.setCharacterStream(2, new CharArrayReader(cbuf), len);
+                psu.setInt(3, 0);
+                rowCount += psu.executeUpdate();
+            } catch (SQLException sqle) {
+                System.out
+                        .println("EXPECTED EXCEPTION - streams cannot be re-used");
+                expectedException(sqle);
+            }
+            System.out.println("DERBY500 for varchar #1 Rows updated  ="
+                    + rowCount);
+
+            //verify data
+            verifyDerby500Test(pss, buf,cbuf, 0, 10, false);
+
+            PreparedStatement psu2 = conn
+                    .prepareStatement("update t1 set vc = ? "
+                            + ", lvc = ? where id = ?  ");
+
+            buf[randomOffset +1] = (byte)'h';
+            cbuf[randomOffset + 1] = 'h';
+            
+            rowCount = 0;
+            try {
+                psu2.setAsciiStream(1, new ByteArrayInputStream(buf), len);
+                psu2.setAsciiStream(2, new ByteArrayInputStream(buf), len);
+                psu2.setInt(3, 0);
+                rowCount += psu2.executeUpdate();
+            } catch (SQLException sqle) {
+                System.out
+                        .println("UNEXPECTED EXCEPTION - update should have actually gone
through");
+                dumpSQLExceptions(sqle);
+            }
+            conn.commit();
+            System.out.println("DERBY500 for varchar #2 Rows updated  ="
+                    + rowCount);
+            verifyDerby500Test(pss, buf,cbuf, 0, 1,false);
+            
+            // delete
+            // as currently we dont allow stream values to be re-used
+            PreparedStatement psd = conn
+                    .prepareStatement("delete from t1 where mvalue = ?");
+
+            rowCount = 0;
+            try {
+                psd.setInt(1, 0);
+                rowCount += psd.executeUpdate();
+                rowCount += psd.executeUpdate();
+            } catch (SQLException sqle) {
+                System.out
+                .println("UNEXPECTED EXCEPTION - delete should have actually gone through");
+                dumpSQLExceptions(sqle);
+            }
+            System.out.println("DERBY500 for varchar #3 Rows deleted ="
+                    + rowCount);
+
+            //verify data
+            verifyDerby500Test(pss, buf,cbuf, 0, 10,false);
+
+            PreparedStatement psd2 = conn
+                    .prepareStatement("delete from t1 where id = ?");
+            
+            rowCount = 0;
+            try {
+                psd2.setInt(1, 0);
+                rowCount += psd2.executeUpdate();
+            } catch (SQLException sqle) {
+                System.out
+                        .println("UNEXPECTED EXCEPTION - delete should have actually gone
through");
+                dumpSQLExceptions(sqle);
+            }
+            conn.commit();
+            System.out.println("DERBY500 for varchar #4 Rows deleted  ="
+                    + rowCount);
+            verifyDerby500Test(pss, buf,cbuf, 1, 2,false);
+
+            stmt.execute("drop table t1");
+            conn.commit();
+            stmt.close();
+            pss.close();
+            psu2.close();
+            psu.close();
+            psd.close();
+            psd2.close();
+            System.out.println("END  DERBY-500 TEST  for varchar");
+            System.out.println("======================================");
+
+        } catch (SQLException sqle) {
+            dumpSQLExceptions(sqle);
+        } catch (Exception e) {
+            System.out.println("DERBY-500 TEST for varchar FAILED!");
+            e.printStackTrace();
+        }
+
+    }
+
+    /**
+     * verify the data in the derby500Test
+     * @param ps select preparedstatement
+     * @param buf byte array to compare the blob data
+     * @param cbuf char array to compare the clob data
+     * @param startId start id of the row to check data for 
+     * @param endId end id of the row to check data for
+     * @param binaryType  flag to indicate if the second column in resultset
+     *                  is a binary type or not. true for binary type 
+     * @throws Exception
+     */
+    private static void verifyDerby500Test(PreparedStatement ps, byte[] buf,char[] cbuf,
+            int startId, int endId,boolean binaryType) throws Exception {
+        byte[] retrieveData = null;
+        int rowCount = 0;
+        ResultSet rs = null;
+        for (int i = startId; i < endId; i++) {
+            ps.setInt(1, i);
+            rs = ps.executeQuery();
+            if(rs.next())
+            {
+            compareCharArray(rs.getCharacterStream(1), cbuf,cbuf.length);
+            if(binaryType)
+                byteArrayEquals(rs.getBytes(2), 0, buf.length, buf, 0, buf.length);
+            else
+                compareCharArray(rs.getCharacterStream(2), cbuf,cbuf.length);
+                
+            rowCount++;
+            }
+        }
+        System.out.println("Rows selected =" + rowCount);
+        rs.close();
+    }
+    /**
+     * compare char data
+     * @param stream data from stream to compare 
+     * @param compare base data to compare against
+     * @param length compare length number of chars.
+     * @throws Exception
+     */
+    private static void compareCharArray(Reader stream, char[] compare,
+            int length) throws Exception {
+        int c1 = 0;
+        int i = 0;
+        do {
+            c1 = stream.read();
+            if (c1 != compare[i++]) {
+                System.out
+                        .println("FAIL -- MISMATCH in data stored versus data retrieved at
"
+                                + (i - 1));
+                break;
+            }
+            length--;
+        } while (c1 != -1 && length > 0);
+
+    }
+    
+    private static void expectedException(SQLException sqle) {
+
+        while (sqle != null) {
+            String sqlState = sqle.getSQLState();
+            if (sqlState == null) {
+                sqlState = "<NULL>";
+            }
+            System.out.println("EXPECTED SQL Exception: (" + sqlState + ") "
+                    + sqle.getMessage());
+
+            sqle = sqle.getNextException();
+        }
+    }
+
 	private static void streamTestDataVerification(ResultSet rs, int maxValueAllowed)
 	throws Exception{
 		ResultSetMetaData met;
@@ -1502,4 +1947,6 @@
 			se = se.getNextException();
 		}
 	}
+    
+    
 }



Mime
View raw message