db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From krist...@apache.org
Subject svn commit: r1330681 - in /db/derby/code/trunk/java: client/org/apache/derby/client/am/ engine/org/apache/derby/impl/jdbc/ testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ testing/org/apache/derbyTesting/functionTests/tests/lang/
Date Thu, 26 Apr 2012 07:20:04 GMT
Author: kristwaa
Date: Thu Apr 26 07:20:03 2012
New Revision: 1330681

URL: http://svn.apache.org/viewvc?rev=1330681&view=rev
Log:
DERBY-5489: getBinary() returns incorrect data after getObject() call on BLOB column

o Added missing check to getObject on LOB columns in the client driver.
o Added new checks to getBytes/getString for LOB columns in both drivers,
  which stands out from the rest of the getters because invoking them multiple
  times on a LOB column is allowed (this is an exception to the rule).
o Added two new tests which verifies that the data return when invoking the
  mentioned getters multiple times returns correct data, including when you
  first invoke getBytes/getString and then invoke one of the other valid
  getters for LOB columns. Some refactoring of LobRsGetterTest.
o Adjusted access pattern in UpdatableResultSetTest to comply with the rules.

At a later time it may be possible to lift this restriction on LOB columns.

Patch file: derby-5489-2b-fixes.diff
--This line, and tose below, will be ignored--

M    java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobRsGetterTest.java
M    java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java
M    java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java
M    java/client/org/apache/derby/client/am/ResultSet.java
M    java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java

Modified:
    db/derby/code/trunk/java/client/org/apache/derby/client/am/ResultSet.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobRsGetterTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/ResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/ResultSet.java?rev=1330681&r1=1330680&r2=1330681&view=diff
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/ResultSet.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/ResultSet.java Thu Apr 26 07:20:03
2012
@@ -1050,11 +1050,18 @@ public abstract class ResultSet implemen
         try
         {
             closeOpenStreams();
-
             if (agent_.loggingEnabled()) {
                 agent_.logWriter_.traceEntry(this, "getString", column);
             }
             checkGetterPreconditions(column, "getString");
+            int type = resultSetMetaData_.types_[column - 1];
+            if (type == Types.BLOB || type == Types.CLOB) {
+                checkLOBMultiCall(column);
+                // If the above didn't fail, this is the first getter
+                // invocation, or only getBytes and/or getString have been
+                // invoked previously. The special treatment of these getters
+                // is allowed for backwards compatibility.
+            }
             String result = null;
             if (wasNonNullSensitiveUpdate(column)) {
                 result = (String) agent_.crossConverters_.setObject(java.sql.Types.CHAR,
updatedColumns_[column - 1]);
@@ -1083,6 +1090,14 @@ public abstract class ResultSet implemen
                 agent_.logWriter_.traceEntry(this, "getBytes", column);
             }
             checkGetterPreconditions(column, "getBytes");
+            int type = resultSetMetaData_.types_[column - 1];
+            if (type == Types.BLOB) {
+                checkLOBMultiCall(column);
+                // If the above didn't fail, this is the first getter
+                // invocation, or only getBytes has been invoked previously.
+                // The special treatment of this getter is allowed for
+                // backwards compatibility.
+            }
             byte[] result = null;
             if (wasNonNullSensitiveUpdate(column)) {
                 result = (byte[]) agent_.crossConverters_.setObject(java.sql.Types.BINARY,
updatedColumns_[column - 1]);
@@ -1354,6 +1369,10 @@ public abstract class ResultSet implemen
     // used by DBMD
     Object getObjectX(int column) throws SqlException {
         checkGetterPreconditions(column, "getObject");
+        int type = resultSetMetaData_.types_[column - 1];
+        if (type == Types.BLOB || type == Types.CLOB) {
+            useStreamOrLOB(column);
+        }
         Object result = null;
         if (wasNonNullSensitiveUpdate(column)) {
             result = updatedColumns_[column - 1];
@@ -5481,6 +5500,26 @@ public abstract class ResultSet implemen
      * @throws SQLException if the column has already been accessed
      */
     void useStreamOrLOB(int columnIndex) throws SqlException {
+        checkLOBMultiCall(columnIndex);
+        columnUsedFlags_[columnIndex - 1] = true;
+    }
+
+    /**
+     * Checks if a stream or a LOB object has already been created for the
+     * specified LOB column.
+     * <p>
+     * Accessing a LOB column more than once is not forbidden by the JDBC
+     * specification, but the Java API states that for maximum portability,
+     * result set columns within each row should be read in left-to-right order,
+     * and each column should be read only once. The restriction was implemented
+     * in Derby due to complexities with the positioning of store streams when
+     * the user was given multiple handles to the stream.
+     *
+     * @param columnIndex 1-based index of the LOB column
+     * @throws SqlException if the column has already been accessed
+     */
+    private void checkLOBMultiCall(int columnIndex)
+            throws SqlException {
         if (columnUsedFlags_ == null) {
             columnUsedFlags_ = new boolean[resultSetMetaData_.columns_];
         }
@@ -5488,11 +5527,8 @@ public abstract class ResultSet implemen
             throw new SqlException(agent_.logWriter_,
                 new ClientMessageId(SQLState.LANG_STREAM_RETRIEVED_ALREADY));
         }
-
-        columnUsedFlags_[columnIndex - 1] = true;
     }
 
-
     /**
      * Clears the flags for used columns, typically invoked when changing the
      * result set position.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java?rev=1330681&r1=1330680&r2=1330681&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java Thu Apr
26 07:20:03 2012
@@ -676,7 +676,14 @@ public abstract class EmbedResultSet ext
      */
     public final String getString(int columnIndex) throws SQLException {
         checkIfClosed("getString");
-
+        int columnType = getColumnType(columnIndex);
+        if (columnType == Types.BLOB || columnType == Types.CLOB) {
+            checkLOBMultiCall(columnIndex);
+            // If the above didn't fail, this is the first getter invocation,
+            // or only getString and/or getBytes have been invoked previously.
+            // The special treatment of these getters is allowed for
+            // backwards compatibility.
+        }
 			try {
 
 				DataValueDescriptor dvd = getColumn(columnIndex);
@@ -687,7 +694,7 @@ public abstract class EmbedResultSet ext
 				String value = dvd.getString();
 
 				// check for the max field size limit 
-                if (maxFieldSize > 0 && isMaxFieldSizeType(getColumnType(columnIndex)))
+                if (maxFieldSize > 0 && isMaxFieldSizeType(columnType))
                 {
                     if (value.length() > maxFieldSize )
                     {
@@ -875,6 +882,13 @@ public abstract class EmbedResultSet ext
      */
     public final byte[] getBytes(int columnIndex) throws SQLException	{
 		checkIfClosed("getBytes");
+        int columnType = getColumnType(columnIndex);
+        if (columnType == Types.BLOB) {
+            checkLOBMultiCall(columnIndex);
+            // If the above didn't fail, this is the first getter invocation,
+            // or only getBytes has been invoked previously. The special
+            // treatment of this getter is allowed for backwards compatibility.
+        }
 		try {
 
 			DataValueDescriptor dvd = getColumn(columnIndex);
@@ -885,7 +899,7 @@ public abstract class EmbedResultSet ext
 			byte[] value = dvd.getBytes();
 
             // check for the max field size limit 
-            if (maxFieldSize > 0 && isMaxFieldSizeType(getColumnType(columnIndex)))
+            if (maxFieldSize > 0 && isMaxFieldSizeType(columnType))
             {
                  if (value.length > maxFieldSize)
                  {
@@ -4640,12 +4654,31 @@ public abstract class EmbedResultSet ext
      * @throws SQLException if the column has already been accessed
      */
     final void useStreamOrLOB(int columnIndex) throws SQLException {
+        checkLOBMultiCall(columnIndex);
+        columnUsedFlags[columnIndex - 1] = true;
+    }
+
+    /**
+     * Checks if a stream or a LOB object has already been created for the
+     * specified LOB column.
+     * <p>
+     * Accessing a LOB column more than once is not forbidden by the JDBC
+     * specification, but the Java API states that for maximum portability,
+     * result set columns within each row should be read in left-to-right order,
+     * and each column should be read only once. The restriction was implemented
+     * in Derby due to complexities with the positioning of store streams when
+     * the user was given multiple handles to the stream.
+     *
+     * @param columnIndex 1-based index of the LOB column
+     * @throws SQLException if the column has already been accessed
+     */
+    private void checkLOBMultiCall(int columnIndex)
+            throws SQLException {
         if (columnUsedFlags == null) {
             columnUsedFlags = new boolean[getMetaData().getColumnCount()];
         } else if (columnUsedFlags[columnIndex - 1]) {
             throw newSQLException(SQLState.LANG_STREAM_RETRIEVED_ALREADY);
         }
-        columnUsedFlags[columnIndex - 1] = true;
     }
 
     /**

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobRsGetterTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobRsGetterTest.java?rev=1330681&r1=1330680&r2=1330681&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobRsGetterTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobRsGetterTest.java
Thu Apr 26 07:20:03 2012
@@ -20,6 +20,9 @@
  */
 package org.apache.derbyTesting.functionTests.tests.jdbcapi;
 
+import java.io.IOException;
+import java.sql.Blob;
+import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -27,6 +30,7 @@ import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Types;
+import java.util.Arrays;
 
 import junit.framework.Test;
 
@@ -100,6 +104,12 @@ public class LobRsGetterTest
         /* getObject            */ { X, X },
     };
 
+    /**
+     * The names of the various getters used in this test.
+     * <p>
+     * The positions/indexes must correspond to those in
+     * {@linkplain #COMPATIBLE_GETTERS}.
+     */
     private static final String[] GETTER_NAMES = new String[] {
         "getBytes", "getString", "getAsciiStream", "getBinaryStream",
         "getCharacterStream", "getClob", "getBlob", "getObject"
@@ -157,7 +167,7 @@ public class LobRsGetterTest
      */
     public void testBlobGettersSimple()
             throws SQLException {
-        testGettersSimple("dBlob", BLOB);
+        _testGettersSimple("dBlob", BLOB);
     }
 
     /**
@@ -165,7 +175,7 @@ public class LobRsGetterTest
      */
     public void testClobGettersSimple()
             throws SQLException {
-        testGettersSimple("dClob", CLOB);
+        _testGettersSimple("dClob", CLOB);
     }
 
     /**
@@ -173,7 +183,7 @@ public class LobRsGetterTest
      */
     public void testBlobGettersSimpleNegative()
             throws SQLException {
-        testGettersSimpleNegative("dBlob", BLOB);
+        _testGettersSimpleNegative("dBlob", BLOB);
     }
 
     /**
@@ -181,7 +191,7 @@ public class LobRsGetterTest
      */
     public void testClobGettersSimpleNegative()
             throws SQLException {
-        testGettersSimpleNegative("dClob", CLOB);
+        _testGettersSimpleNegative("dClob", CLOB);
     }
 
     /**
@@ -193,7 +203,7 @@ public class LobRsGetterTest
      */
     public void testBlobGettersMultiInvocation()
             throws SQLException {
-        testGettersMultiInvocation("dBlob", BLOB);
+        _testGettersMultiInvocation("dBlob", BLOB);
     }
 
     /**
@@ -204,7 +214,7 @@ public class LobRsGetterTest
      */
     public void testClobGettersMultiInvocation()
             throws SQLException {
-        testGettersMultiInvocation("dClob", CLOB);
+        _testGettersMultiInvocation("dClob", CLOB);
     }
 
     /**
@@ -227,7 +237,6 @@ public class LobRsGetterTest
                 invokeGetter(rs, BLOB, GET_BYTES);
                 assertTrue("getBytes should have failed after: " +
                         debugInfo(1, rs, BLOB, getter),
-                        // TODO: Is GET_STRING to be considerd valid for BLOB?
                         getter == GET_BYTES || getter == GET_STRING);
             } catch (SQLException sqle) {
                 assertTrue(getter != GET_BYTES);
@@ -270,7 +279,179 @@ public class LobRsGetterTest
         }
     }
 
-    private void testGettersMultiInvocation(String columnName, int typeIdx)
+    /**
+     * Tests that data returned by the last BLOB getter invokation is correct.
+     */
+    public void testCorrectBlobDataWithMultiCall()
+            throws IOException, SQLException {
+        setAutoCommit(false);
+        PreparedStatement psId = prepareStatement("select id from " + TABLE);
+        String select = "select dBlob from " + TABLE + " where id = ?";
+        PreparedStatement ps1 = prepareStatement(select);
+        PreparedStatement ps2 = prepareStatement(select);
+        ResultSet rsId = psId.executeQuery();
+        ResultSet rs1;
+        ResultSet rs2;
+        while (rsId.next()) {
+            ps1.setInt(1, rsId.getInt(1));
+            ps2.setInt(1, rsId.getInt(1));
+
+            // getBytes - getString - getBinaryStream
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getBytes(1);
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertEquals(rs2.getBinaryStream(1), rs1.getBinaryStream(1));
+            rs1.close();
+            rs2.close();
+
+            // getString - getBytes - getBlob
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getString(1);
+            rs1.getBytes(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertEquals(rs2.getBlob(1), rs1.getBlob(1));
+            rs1.close();
+            rs2.close();
+
+            // getBytes - getString - getCharacterStream
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getBytes(1);
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertEquals(rs2.getCharacterStream(1), rs1.getCharacterStream(1));
+            rs1.close();
+            rs2.close();
+
+            // getBytes - getString - getBytes
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getBytes(1);
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertTrue(Arrays.equals(rs2.getBytes(1), rs1.getBytes(1)));
+            rs1.close();
+            rs2.close();
+
+            // getBytes - getString - getString
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getBytes(1);
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertEquals(rs2.getString(1), rs1.getString(1));
+            rs1.close();
+            rs2.close();
+
+            // getString - getBytes - getObject
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getString(1);
+            rs1.getBytes(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            Blob b1 = (Blob)rs1.getObject(1);
+            Blob b2 = (Blob)rs2.getObject(1);
+            assertEquals(b2, b1);
+            rs1.close();
+            rs2.close();
+
+            // getBytes - getString - getAsciiStream
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getBytes(1);
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertEquals(rs2.getAsciiStream(1), rs1.getAsciiStream(1));
+            rs1.close();
+            rs2.close();
+        }
+        rollback();
+    }
+
+    /**
+     * Tests that data returned by the last CLOB getter invokation is correct.
+     */
+    public void testCorrectClobDataWithMultiCall()
+            throws IOException, SQLException {
+        setAutoCommit(false);
+        PreparedStatement psId = prepareStatement(
+                "select id, dClob from " + TABLE);
+        String select = "select dClob from " + TABLE + " where id = ?";
+        PreparedStatement ps1 = prepareStatement(select);
+        PreparedStatement ps2 = prepareStatement(select);
+        ResultSet rsId = psId.executeQuery();
+        ResultSet rs1;
+        ResultSet rs2;
+        while (rsId.next()) {
+            ps1.setInt(1, rsId.getInt(1));
+            ps2.setInt(1, rsId.getInt(1));
+
+            // getString - getString
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertEquals(rs2.getString(1), rs1.getString(1));
+            rs1.close();
+            rs2.close();
+
+            // getString - getCharacterStream
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertEquals(rs2.getCharacterStream(1), rs1.getCharacterStream(1));
+            rs1.close();
+            rs2.close();
+
+            // getString - getClob
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertEquals(rs2.getClob(1), rs1.getClob(1));
+            rs1.close();
+            rs2.close();
+
+            // getString - getObject
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            Clob b1 = (Clob)rs1.getObject(1);
+            Clob b2 = (Clob)rs2.getObject(1);
+            assertEquals(b2, b1);
+            rs1.close();
+            rs2.close();
+
+            // getString - getAsciiStream
+            rs1 = ps1.executeQuery();
+            assertTrue(rs1.next());
+            rs1.getString(1);
+            rs2 = ps2.executeQuery();
+            assertTrue(rs2.next());
+            assertEquals(rs2.getAsciiStream(1), rs1.getAsciiStream(1));
+            rs1.close();
+            rs2.close();
+        }
+        rollback();
+    }
+
+    private void _testGettersMultiInvocation(String columnName, int typeIdx)
             throws SQLException {
         PreparedStatement ps = prepareStatement(
                 "select " + columnName + " from " + TABLE);
@@ -294,7 +475,7 @@ public class LobRsGetterTest
         }
     }
 
-    private void testGettersSimpleNegative(String columnName, int typeIdx)
+    private void _testGettersSimpleNegative(String columnName, int typeIdx)
             throws SQLException {
         PreparedStatement ps = prepareStatement(
                 "select " + columnName + " from " + TABLE);
@@ -319,7 +500,7 @@ public class LobRsGetterTest
         }
     }
 
-    private void testGettersSimple(String columnName, int typeIdx)
+    private void _testGettersSimple(String columnName, int typeIdx)
             throws SQLException {
         PreparedStatement ps = prepareStatement(
                 "select " + columnName + " from " + TABLE);

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java?rev=1330681&r1=1330680&r2=1330681&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java
Thu Apr 26 07:20:03 2012
@@ -96,7 +96,8 @@ public class _Suite extends BaseTestCase
         suite.addTest(ResultSetStreamTest.suite());
         suite.addTest(InternationalConnectSimpleDSTest.suite());
         suite.addTest(Derby2017LayerATest.suite());
-        
+        suite.addTest(LobRsGetterTest.suite());
+
         // Old harness .java tests that run using the HarnessJavaTest
         // adapter and continue to use a single master file.
         suite.addTest(JDBCHarnessJavaTest.suite());

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java?rev=1330681&r1=1330680&r2=1330681&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java
Thu Apr 26 07:20:03 2012
@@ -3177,18 +3177,20 @@ public class UpdatableResultSetTest  ext
         if (usingEmbedded() && JDBC.vmSupportsJDBC3()) {
             println("  updateClob and then cancelRowUpdates");
             String clb1 = rs.getString(13);
+            String clb2 = rs1.getString(13);
             rs.updateClob(13, rs1.getClob(13));
             assertEquals("FAIL - wrong value returned by getXXX method",
-                    rs1.getString(13), rs.getString(13));
+                    clb2, rs.getString(13));
             rs.cancelRowUpdates();
             assertEquals("FAIL - wrong value returned by getXXX method",
                     clb1, rs.getString(13));
             
             println("  updateBlob and then cancelRowUpdates");
             bts = rs.getBytes(17);
+            byte[] bts2 = rs1.getBytes(17);
             rs.updateBlob(17,rs1.getBlob(17));
             assertTrue("FAIL - wrong value returned by getXXX method",
-                    java.util.Arrays.equals(rs.getBytes(17),rs1.getBytes(17)));
+                    java.util.Arrays.equals(rs.getBytes(17), bts2));
             rs.cancelRowUpdates();
             assertTrue("FAIL - wrong value returned by getXXX method",
                     java.util.Arrays.equals(rs.getBytes(17),bts));



Mime
View raw message