Index: java/drda/org/apache/derby/impl/drda/EXTDTAInputStream.java =================================================================== --- java/drda/org/apache/derby/impl/drda/EXTDTAInputStream.java (revision 545663) +++ java/drda/org/apache/derby/impl/drda/EXTDTAInputStream.java (working copy) @@ -20,23 +20,20 @@ */ package org.apache.derby.impl.drda; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.BufferedInputStream; +import java.io.Reader; import java.sql.ResultSet; import java.sql.Blob; import java.sql.Clob; import java.sql.SQLException; -import java.io.UnsupportedEncodingException; - import org.apache.derby.iapi.reference.DRDAConstants; import org.apache.derby.iapi.services.sanity.SanityManager; import org.apache.derby.impl.jdbc.Util; /** - * @author marsden * * EXTDTAObjectHolder provides Externalized Large Object representation that * does not hold locks until the end of the transaction (DERBY-255) @@ -51,10 +48,10 @@ private InputStream binaryInputStream = null; private boolean isEmptyStream; + - private ResultSet dataResultSet = null; - private Blob blob = null; - private Clob clob = null; + + private EXTDTAInputStream(ResultSet rs, int columnNumber, @@ -62,7 +59,7 @@ throws SQLException, IOException { - this.dataResultSet = rs; + this.isEmptyStream = ! initInputStream(rs, columnNumber, ndrdaType); @@ -111,40 +108,7 @@ } - /** - * Get the length of the InputStream - * This method is currently not used because there seems to be no way to - * reset the she stream. - * - * @param binaryInputStream - * an InputStream whose length needs to be calclulated - * @return length of stream - */ - private static long getInputStreamLength(InputStream binaryInputStream) - throws SQLException { - long length = 0; - if (binaryInputStream == null) - return length; - - try { - for (;;) { - int avail = binaryInputStream.available(); - binaryInputStream.skip(avail); - if (avail == 0) - break; - length += avail; - - } - //binaryInputStream.close(); - } catch (IOException ioe) { - throw Util.javaException(ioe); - } - - return length; - - } - /** * * @@ -170,17 +134,9 @@ */ public void close() throws IOException { - try{ if (binaryInputStream != null) binaryInputStream.close(); binaryInputStream = null; - - }finally{ - - blob = null; - clob = null; - dataResultSet = null; - } } @@ -282,29 +238,27 @@ { InputStream is = null; - try{ - // BLOBS - if (ndrdaType == DRDAConstants.DRDA_TYPE_NLOBBYTES) - { - blob = rs.getBlob(column); - if(blob == null){ - return false; - } - - is = blob.getBinaryStream(); - - } + Reader r = null; + // BLOBS + if (ndrdaType == DRDAConstants.DRDA_TYPE_NLOBBYTES) + { + is = rs.getBinaryStream(column); + if(is == null){ + return false; + } + } // CLOBS - else if (ndrdaType == DRDAConstants.DRDA_TYPE_NLOBCMIXED) - { - try { - clob = rs.getClob(column); - - if(clob == null){ - return false; - } + else if (ndrdaType == DRDAConstants.DRDA_TYPE_NLOBCMIXED) + { + try { + + r = rs.getCharacterStream(column); + + if(r == null){ + return false; + } - is = new ReEncodedInputStream(clob.getCharacterStream()); + is = new ReEncodedInputStream(r); }catch (java.io.UnsupportedEncodingException e) { throw new SQLException (e.getMessage()); @@ -323,71 +277,20 @@ " not valid EXTDTA object type"); } } - - boolean exist = is.read() > -1; - - is.close(); - is = null; - - if(exist){ - openInputStreamAgain(); - } - - return exist; - - }catch(IllegalStateException e){ - throw Util.javaException(e); - - }finally{ - if(is != null) - is.close(); - - } + if(! is.markSupported() ) + is = new BufferedInputStream(is); + is.mark(2); + // Need to read first byte to determine if this is an empty stream + // no EXTDTA will be sent for 0 length streams. + int firstRead = is.read(); + boolean exist = firstRead > -1; + is.reset(); + this.binaryInputStream= is; + return exist; } - - /** - * - * This method is called from initInputStream and - * opens inputstream again to stream actually. - * - */ - private void openInputStreamAgain() throws IllegalStateException,SQLException { - - if(this.binaryInputStream != null){ - return; - } - - InputStream is = null; - try{ - - if(SanityManager.DEBUG){ - SanityManager.ASSERT( ( blob != null && clob == null ) || - ( clob != null && blob == null ), - "One of blob or clob must be non-null."); - } - - if(blob != null){ - is = blob.getBinaryStream(); - - }else if(clob != null){ - is = new ReEncodedInputStream(clob.getCharacterStream()); - } - - }catch(IOException e){ - throw new IllegalStateException(e.getMessage()); - } - - if(! is.markSupported() ){ - is = new BufferedInputStream(is); - } - - this.binaryInputStream = is; - - } - - + protected void finalize() throws Throwable{ close(); } Index: java/drda/org/apache/derby/impl/drda/DRDAConnThread.java =================================================================== --- java/drda/org/apache/derby/impl/drda/DRDAConnThread.java (revision 545663) +++ java/drda/org/apache/derby/impl/drda/DRDAConnThread.java (working copy) @@ -6531,7 +6531,7 @@ if (SanityManager.DEBUG) trace("!!drdaType = " + java.lang.Integer.toHexString(drdaType) + - "precision = " + precision +" scale = " + scale); + " precision=" + precision +" scale = " + scale); switch (ndrdaType) { case DRDAConstants.DRDA_TYPE_NLOBBYTES: Index: java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LargeDataLocks.java =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LargeDataLocks.java (revision 0) +++ java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LargeDataLocks.java (revision 0) @@ -0,0 +1,233 @@ +package org.apache.derbyTesting.functionTests.tests.jdbcapi; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.apache.derby.tools.ij; +import org.apache.derbyTesting.functionTests.util.Formatters; + + +/* + * Derby - Class org.apache.derbyTesting.functionTests.tests.jdbcapi.LargeDataLocks + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +/** + * Test for DERBY-2892/DERBY-255 to ensure that ResultSet methods + * getString(), getCharacterStream, getBytes() and getBinaryStream() + * do not hold locks until the end of the transaction. + * + */ + +public class LargeDataLocks { + + /** + * @param args + */ + public static void main(String[] args) { + + Connection conn = null; + // TODO Auto-generated method stub + try + { + // use the ij utility to read the property file and + // make the initial connection. + ij.getPropertyArg(args); + conn = ij.startJBMS(); + conn.setAutoCommit(false); + PreparedStatement ps = null; + String sql; + Statement stmt = conn.createStatement(); + sql = "CREATE TABLE t1 (bc CLOB(1M), bincol BLOB(1M), datalen int)"; + stmt.executeUpdate(sql); + + // Insert big and little values + sql = "INSERT into t1 values(?,?,?)"; + ps = conn.prepareStatement(sql); + + ps.setCharacterStream(1, new java.io.StringReader(Formatters + .repeatChar("a", 38000)), 38000); + ps.setBytes(2, Formatters.repeatChar("a", 38000).getBytes()); + ps.setInt(3, 38000); + ps.executeUpdate(); + ps.close(); + stmt.close(); + conn.commit(); + + testGetCharacterStream(conn); + + testGetString(conn); + testGetBinaryStream(conn); + testGetBytes(conn); + } catch (Exception e) { + + e.printStackTrace(); + } + } + /** + * Test that ResultSet.getCharacterStream does not hold locks after the + * ResultSet is closed + * + * @param conn Connection to use + * @throws SQLException + * @throws IOException + * @throws InstantiationException + * @throws ClassNotFoundException + * @throws IllegalAccessException + */ + public static void testGetCharacterStream(Connection conn) throws SQLException, IOException, IllegalAccessException, ClassNotFoundException, InstantiationException { + // getCharacterStream() no locks expected after retrieval + int numChars = 0; + Statement stmt = conn.createStatement(); + String sql = "SELECT bc from t1"; + // First with getCharacterStream + ResultSet rs = stmt.executeQuery(sql); + rs.next(); + java.io.Reader characterStream = rs.getCharacterStream(1); + // Extract all the characters + int read = characterStream.read(); + while (read != -1) { + read = characterStream.read(); + numChars++; + } + assertEquals(38000, numChars); + rs.close(); + assertEquals(0, countLocks()); + stmt.close(); + conn.commit(); + } + + /** + * Verify that getBytes does not hold locks after ResultSet is closed. + * + * @param conn Connection to use + * @throws SQLException + * @throws InstantiationException + * @throws ClassNotFoundException + * @throws IllegalAccessException + */ + public static void testGetBytes(Connection conn) throws SQLException, IllegalAccessException, ClassNotFoundException, InstantiationException { + // getBytes() no locks expected after retrieval + Statement stmt = conn.createStatement(); + String sql = "SELECT bincol from t1"; + ResultSet rs = stmt.executeQuery(sql); + rs.next(); + byte[] value = rs.getBytes(1); + assertEquals(38000, value.length); + rs.close(); + assertEquals(0, countLocks()); + conn.commit(); + + } + + /** + * Verify that getBinaryStream() does not hold locks after retrieval + * + * @param conn Connection to use + * @throws SQLException + * @throws IOException + * @throws InstantiationException + * @throws ClassNotFoundException + * @throws IllegalAccessException + */ + public static void testGetBinaryStream(Connection conn) throws SQLException, IOException, IllegalAccessException, ClassNotFoundException, InstantiationException { + int numBytes = 0; + conn.setAutoCommit(false); + Statement stmt = conn.createStatement(); + String sql = "SELECT bincol from t1"; + ResultSet rs = stmt.executeQuery(sql); + rs.next(); + InputStream stream = rs.getBinaryStream(1); + int read = stream.read(); + while (read != -1) { + read = stream.read(); + numBytes++; + } + assertEquals(38000, numBytes); + rs.close(); + stmt.close(); + assertEquals(0, countLocks()); + conn.commit(); + } + + /** + * Test that ResultSet.getString() does not hold locks after the ResultSet + * is closed + * @param conn Connnection to use + * @throws SQLException + * @throws IOException + * @throws InstantiationException + * @throws ClassNotFoundException + * @throws IllegalAccessException + */ + public static void testGetString(Connection conn) throws SQLException, IOException, IllegalAccessException, ClassNotFoundException, InstantiationException { + // getString() no locks expected after retrieval + Statement stmt = conn.createStatement(); + String sql = "SELECT bc from t1"; + ResultSet rs = stmt.executeQuery(sql); + rs.next(); + String value = rs.getString(1); + assertEquals(38000, value.length()); + rs.close(); + stmt.close(); + assertEquals(0, countLocks()); + conn.commit(); + } + + /** + * Create a new connection and count the number of locks held. + * + * @return number of locks held + * + * @throws SQLExceptions + * @throws InstantiationException + * @throws ClassNotFoundException + * @throws IllegalAccessException + */ + public static int countLocks() throws SQLException, IllegalAccessException, ClassNotFoundException, InstantiationException { + Connection conn = ij.startJBMS(); + String sql; + Statement stmt = conn.createStatement(); + + sql = "Select count(*) from new org.apache.derby.diag.LockTable() as LT"; + ResultSet lockrs = stmt.executeQuery(sql); + lockrs.next(); + int count = lockrs.getInt(1); + lockrs.close(); + stmt.close(); + conn.close(); + return count; + } + + + private static void assertEquals(int expected, int value) { + if (expected != value) + { + System.out.println("FAIL: " + value + " does not match expected value: " + expected); + new Exception().printStackTrace(); + } + } + +} Property changes on: java\testing\org\apache\derbyTesting\functionTests\tests\jdbcapi\LargeDataLocks.java ___________________________________________________________________ Name: svn:eol-style + native Index: java/testing/org/apache/derbyTesting/functionTests/suites/jdbcapi.runall =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/suites/jdbcapi.runall (revision 545663) +++ java/testing/org/apache/derbyTesting/functionTests/suites/jdbcapi.runall (working copy) @@ -12,6 +12,7 @@ jdbcapi/secureUsers1.sql jdbcapi/maxfieldsize.java jdbcapi/LOBTest.java +jdbcapi/LargeDataLocks.java jdbcapi/blobclob4BLOB.java jdbcapi/parameterMapping.java jdbcapi/setTransactionIsolation.java