You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by km...@apache.org on 2013/04/09 20:40:45 UTC

svn commit: r1466174 - in /db/derby/code/branches/10.8.3.1_testcompat: ./ java/client/org/apache/derby/client/am/ java/engine/org/apache/derby/impl/jdbc/ java/testing/org/apache/derbyTesting/functionTests/tests/lang/

Author: kmarsden
Date: Tue Apr  9 18:40:45 2013
New Revision: 1466174

URL: http://svn.apache.org/r1466174
Log:
DERBY-5489 prevent double retrieve on lob column to prevent wrong results.

partial merge of tests revision 1330681


Modified:
    db/derby/code/branches/10.8.3.1_testcompat/   (props changed)
    db/derby/code/branches/10.8.3.1_testcompat/java/client/org/apache/derby/client/am/ResultSet.java
    db/derby/code/branches/10.8.3.1_testcompat/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
    db/derby/code/branches/10.8.3.1_testcompat/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java

Propchange: db/derby/code/branches/10.8.3.1_testcompat/
------------------------------------------------------------------------------
  Merged /db/derby/code/trunk:r1330681

Modified: db/derby/code/branches/10.8.3.1_testcompat/java/client/org/apache/derby/client/am/ResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.8.3.1_testcompat/java/client/org/apache/derby/client/am/ResultSet.java?rev=1466174&r1=1466173&r2=1466174&view=diff
==============================================================================
--- db/derby/code/branches/10.8.3.1_testcompat/java/client/org/apache/derby/client/am/ResultSet.java (original)
+++ db/derby/code/branches/10.8.3.1_testcompat/java/client/org/apache/derby/client/am/ResultSet.java Tue Apr  9 18:40:45 2013
@@ -1057,11 +1057,18 @@ public abstract class ResultSet implemen
         try
         {
             closeCloseFilterInputStream();
-
             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]);
@@ -1090,6 +1097,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]);
@@ -1360,6 +1375,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];
@@ -5472,6 +5491,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_];
         }
@@ -5479,11 +5518,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/branches/10.8.3.1_testcompat/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.8.3.1_testcompat/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java?rev=1466174&r1=1466173&r2=1466174&view=diff
==============================================================================
--- db/derby/code/branches/10.8.3.1_testcompat/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java (original)
+++ db/derby/code/branches/10.8.3.1_testcompat/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java Tue Apr  9 18:40:45 2013
@@ -677,7 +677,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);
@@ -688,7 +695,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 )
                     {
@@ -876,6 +883,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);
@@ -886,7 +900,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)
                  {
@@ -4639,12 +4653,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/branches/10.8.3.1_testcompat/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.8.3.1_testcompat/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java?rev=1466174&r1=1466173&r2=1466174&view=diff
==============================================================================
--- db/derby/code/branches/10.8.3.1_testcompat/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java (original)
+++ db/derby/code/branches/10.8.3.1_testcompat/java/testing/org/apache/derbyTesting/functionTests/tests/lang/UpdatableResultSetTest.java Tue Apr  9 18:40:45 2013
@@ -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));