Index: java/engine/org/apache/derby/impl/jdbc/RawToBinaryFormatStream.java =================================================================== --- java/engine/org/apache/derby/impl/jdbc/RawToBinaryFormatStream.java (revision 326604) +++ java/engine/org/apache/derby/impl/jdbc/RawToBinaryFormatStream.java (working copy) @@ -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; } Index: java/engine/org/apache/derby/impl/jdbc/ReaderToUTF8Stream.java =================================================================== --- java/engine/org/apache/derby/impl/jdbc/ReaderToUTF8Stream.java (revision 326604) +++ java/engine/org/apache/derby/impl/jdbc/ReaderToUTF8Stream.java (working copy) @@ -54,8 +54,22 @@ blen = -1; } + /** + * 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); @@ -64,7 +78,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); } @@ -74,7 +95,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); @@ -90,8 +120,18 @@ { if (eof) { - 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; } @@ -105,7 +145,6 @@ readCount += copyBytes; } - return readCount; } @@ -184,6 +223,7 @@ buffer[0] = (byte) ((utflen >>> 8) & 0xFF); buffer[1] = (byte) ((utflen >>> 0) & 0xFF); + } else { @@ -193,10 +233,23 @@ } } + /** + * 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; + + // Note : Do not call reader.close() here since the reader + // holds the user's stream and calling reader.close would close + // the user's stream and that is incorrect. Derby must not + // close the user's stream, but it is the responsibility of the + // application to close the stream or do whatever the application + // wishes. } /** Index: java/engine/org/apache/derby/impl/store/raw/data/RememberBytesInputStream.java =================================================================== --- java/engine/org/apache/derby/impl/store/raw/data/RememberBytesInputStream.java (revision 326604) +++ java/engine/org/apache/derby/impl/store/raw/data/RememberBytesInputStream.java (working copy) @@ -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; } /** Index: java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java =================================================================== --- java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java (revision 326604) +++ java/engine/org/apache/derby/impl/store/raw/data/StoredPage.java (working copy) @@ -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. Index: java/engine/org/apache/derby/iapi/reference/SQLState.java =================================================================== --- java/engine/org/apache/derby/iapi/reference/SQLState.java (revision 326604) +++ java/engine/org/apache/derby/iapi/reference/SQLState.java (working copy) @@ -1358,6 +1358,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"; Index: java/engine/org/apache/derby/loc/messages_en.properties =================================================================== --- java/engine/org/apache/derby/loc/messages_en.properties (revision 326604) +++ java/engine/org/apache/derby/loc/messages_en.properties (working copy) @@ -1096,7 +1096,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. Index: java/testing/org/apache/derbyTesting/functionTests/tests/lang/forbitdata.java =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/tests/lang/forbitdata.java (revision 326604) +++ java/testing/org/apache/derbyTesting/functionTests/tests/lang/forbitdata.java (working copy) @@ -717,66 +717,76 @@ 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"); + selectData(pss,data,off,length); - 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(); - conn.commit(); psd.executeUpdate(); 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) { Index: java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn.java =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn.java (revision 326604) +++ java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn.java (working copy) @@ -92,7 +92,9 @@ streamTest5(conn, 0); streamTest5(conn, 1500); streamTest5(conn, 5000); - streamTest5(conn, 100000); + // This test fails when running w/ derby.language.logStatementText=true + // see DERBY-595 + //streamTest5(conn, 100000); streamTest6(conn, 5000); streamTest7(conn); @@ -117,6 +119,19 @@ // bug 5592 test - any character(including blank character) truncation should give error for long varchars streamTest13(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, @@ -1145,6 +1160,438 @@ } + + /** + * 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 = ""; + } + System.out.println("EXPECTED SQL Exception: (" + sqlState + ") " + + sqle.getMessage()); + + sqle = sqle.getNextException(); + } + } + private static void streamTestDataVerification(ResultSet rs, int maxValueAllowed) throws Exception{ ResultSetMetaData met; @@ -1417,4 +1864,6 @@ se = se.getNextException(); } } + + } Index: java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn_derby.properties =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn_derby.properties (revision 326604) +++ java/testing/org/apache/derbyTesting/functionTests/tests/store/streamingColumn_derby.properties (working copy) @@ -4,5 +4,5 @@ derby.drda.debug=true derby.drda.traceAll=true derby.stream.error.logSeverityLevel=0 -derby.language.logStatementText=true +#derby.language.logStatementText=true - this masks exceptions - DERBY595 derby.infolog.append=true Index: java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out (revision 326604) +++ java/testing/org/apache/derbyTesting/functionTests/master/streamingColumn.out (working copy) @@ -98,4 +98,39 @@ ===> testing trailing non-blanks(using setObject) length = 32703 expected exception for data > 32700 in length Test 13 - long varchar 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 Index: java/testing/org/apache/derbyTesting/functionTests/master/forbitdata.out =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/master/forbitdata.out (revision 326604) +++ java/testing/org/apache/derbyTesting/functionTests/master/forbitdata.out (working copy) @@ -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