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 kr...@apache.org on 2012/04/26 09:20:04 UTC
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/
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));