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 ka...@apache.org on 2006/05/14 09:54:58 UTC
svn commit: r406279 - in /db/derby/code/trunk/java:
client/org/apache/derby/client/am/ResultSet.java
engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SURTest.java
Author: kahatlen
Date: Sun May 14 00:54:58 2006
New Revision: 406279
URL: http://svn.apache.org/viewcvs?rev=406279&view=rev
Log:
DERBY-1251: cancelRowUpdates() affects rows updated with updateRow()
in scrollable updatable resultsets
Fix contributed by Andreas Korneliussen.
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/SURTest.java
Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/ResultSet.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/client/org/apache/derby/client/am/ResultSet.java?rev=406279&r1=406278&r2=406279&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 Sun May 14 00:54:58 2006
@@ -160,8 +160,6 @@
protected long absoluteRowNumberForTheIntendedRow_;
- // This variable helps keep track of whether cancelRowUpdates() should have any effect.
- protected boolean updateRowCalled_ = false;
private boolean isOnInsertRow_ = false; // reserved for later
protected boolean isOnCurrentRow_ = true;
public int rowsReceivedInCurrentRowset_ = 0; // keep track of the number of rows received in the
@@ -3552,7 +3550,7 @@
}
// need to cancel updates if the actual update was not successful at the server.
// alternative is to check for updateCount_ in "positionToCurrentRowAndUpdate".
- // cancelRowUpdates if updateCount_ != 1, else set updateRowCalled_ to true.
+ // cancelRowUpdates if updateCount_ != 1
try {
if (isRowsetCursor_ ||
sensitivity_ == sensitivity_sensitive_dynamic__ ||
@@ -3561,19 +3559,16 @@
} else {
positionToCurrentRowAndUpdate();
}
- updateRowCalled_ = true;
- } catch (SqlException e) {
- try {
- cancelRowUpdates();
- } catch ( SQLException se ) {
- throw new SqlException(se);
- }
- throw e;
+ } finally {
+ resetUpdatedColumns();
}
- // other result set types don't implement detectability
+ // Ensure the data is reset
if (resultSetType_ == ResultSet.TYPE_SCROLL_INSENSITIVE) {
- cursor_.setIsRowUpdated(true);
+ if (preparedStatementForUpdate_.updateCount_ > 0) {
+ // This causes a round-trip
+ getAbsoluteRowset(absolutePosition_);
+ }
}
return true;
@@ -3624,12 +3619,17 @@
positionToCurrentRowAndDelete();
}
- Boolean nullIndicator = Cursor.ROW_IS_NULL;
if (resultSetType_ == java.sql.ResultSet.TYPE_FORWARD_ONLY) {
cursor_.isUpdateDeleteHole_ = true;
} else {
- cursor_.isUpdateDeleteHoleCache_.set((int) currentRowInRowset_, nullIndicator);
- cursor_.isUpdateDeleteHole_ = ((Boolean) cursor_.isUpdateDeleteHoleCache_.get((int) currentRowInRowset_)).booleanValue();
+ if (preparedStatementForDelete_.updateCount_ > 0) {
+
+ cursor_.isUpdateDeleteHoleCache_.set((int) currentRowInRowset_,
+ Cursor.ROW_IS_NULL);
+ cursor_.isUpdateDeleteHole_ =
+ ((Boolean) cursor_.isUpdateDeleteHoleCache_.
+ get((int) currentRowInRowset_)).booleanValue();
+ }
}
}
@@ -3690,12 +3690,8 @@
if (!isValidCursorPosition_)
throw new SqlException(agent_.logWriter_,
new ClientMessageId(SQLState.CURSOR_INVALID_OPERATION_AT_CURRENT_POSITION));
-
- // if updateRow() has already been called, then cancelRowUpdates should have
- // no effect. updateRowCalled_ is reset to false as soon as the cursor moves to a new row.
- if (!updateRowCalled_) {
- resetUpdatedColumns();
- }
+ // Reset updated columns
+ resetUpdatedColumns();
}
}
catch ( SqlException se )
@@ -4554,7 +4550,6 @@
columnUpdated_[i] = false;
}
}
- updateRowCalled_ = false;
}
private final long getRowUncast() {
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java?rev=406279&r1=406278&r2=406279&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 Sun May 14 00:54:58 2006
@@ -29,6 +29,7 @@
import org.apache.derby.iapi.sql.ResultSet;
import org.apache.derby.iapi.sql.ParameterValueSet;
+import org.apache.derby.iapi.sql.execute.ExecutionFactory;
import org.apache.derby.iapi.sql.execute.ExecCursorTableReference;
import org.apache.derby.iapi.sql.execute.ExecRow;
import org.apache.derby.iapi.sql.execute.NoPutResultSet;
@@ -97,10 +98,12 @@
protected static final int ABSOLUTE = 7;
protected static final int RELATIVE = 8;
- // mutable state
- protected ExecRow currentRow;
- //deleteRow & updateRow make rowData null so that ResultSet is not positioned on deleted/updated row.
- private DataValueDescriptor[] rowData;
+ /**
+ * The currentRow contains the data of the current row of the resultset.
+ * If the containing row array is null, the cursor is not postioned on a
+ * row
+ */
+ private final ExecRow currentRow;
protected boolean wasNull;
/**
@@ -114,8 +117,6 @@
boolean isClosed;
private boolean isOnInsertRow;
- private ExecRow currentRowBeforeInsert;
- private ExecRow insertRow = null;
private Object currentStream;
// immutable state
@@ -180,10 +181,15 @@
private final int concurrencyOfThisResultSet;
- //copyOfDatabaseRow will keep the original contents of the columns of the current row which got updated.
- //These will be used if user decides to cancel the changes made to the row using cancelRowUpdates.
- private DataValueDescriptor[] copyOfDatabaseRow;
- private boolean[] columnGotUpdated; //these are the columns which have been updated so far. Used to build UPDATE...WHERE CURRENT OF sql
+ /* updateRow is used to keep the values which are updated with updateXXX()
+ * calls. It is used by both insertRow() and updateRow().
+ * It is initialized to null if the resultset is not updatable.
+ */
+ private final ExecRow updateRow;
+
+ /* These are the columns which have been updated so far.
+ */
+ private boolean[] columnGotUpdated;
private boolean currentRowHasBeenUpdated; //Gets set to true after first updateXXX on a row. Gets reset to false when the cursor moves off the row
private int fetchDirection;
@@ -240,14 +246,26 @@
// Fill in the column types
resultDescription = theResults.getResultDescription();
+ final ExecutionFactory factory = conn.getLanguageConnection().
+ getLanguageConnectionFactory().getExecutionFactory();
+ final int columnCount = getMetaData().getColumnCount();
+ this.currentRow = factory.getValueRow(columnCount);
+ currentRow.setRowArray(null);
// Only incur the cost of allocating and maintaining
// updated column information if the columns can be updated.
if (concurrencyOfThisResultSet == JDBC20Translation.CONCUR_UPDATABLE)
{
- //initialize arrays related to updateRow implementation
- columnGotUpdated = new boolean[getMetaData().getColumnCount()];
- copyOfDatabaseRow = new DataValueDescriptor[columnGotUpdated.length];
+ //initialize arrays related to updateRow implementation
+ columnGotUpdated = new boolean[columnCount];
+ updateRow = factory.getValueRow(columnCount);
+ for (int i = 1; i <= columnCount; i++) {
+ updateRow.setColumn(i, resultDescription.getColumnDescriptor(i).
+ getType().getNull());
+ }
+ initializeUpdateRowModifiers();
+ } else {
+ updateRow = null;
}
// assign the max rows and maxfiled size limit for this result set
@@ -286,17 +304,22 @@
}
}
- // onRow protects us from making requests of
+ // checkOnRow protects us from making requests of
// resultSet that would fail with NullPointerExceptions
// or milder problems due to not having a row.
- protected final DataValueDescriptor[] checkOnRow() throws SQLException {
-
- DataValueDescriptor[] theCurrentRow = rowData;
-
- if (theCurrentRow == null)
+ protected final void checkOnRow() throws SQLException
+ {
+ if (currentRow.getRowArray() == null) {
throw newSQLException(SQLState.NO_CURRENT_ROW);
+ }
+ }
- return theCurrentRow;
+ /**
+ * Initializes the currentRowHasBeenUpdated and columnGotUpdated fields
+ */
+ private void initializeUpdateRowModifiers() {
+ currentRowHasBeenUpdated = false;
+ Arrays.fill(columnGotUpdated, false);
}
/**
@@ -305,7 +328,7 @@
@exception SQLException ResultSet is not on a row or columnIndex is out of range.
*/
final int getColumnType(int columnIndex) throws SQLException {
- checkOnRow(); // first make sure there's a row
+ if (!isOnInsertRow) checkOnRow(); // first make sure there's a row
if (columnIndex < 1 ||
columnIndex > resultDescription.getColumnCount())
@@ -346,14 +369,6 @@
return false;
}
}
-
- if (columnGotUpdated != null)
- {
- //since we are moving off of the current row, need to initialize state corresponding to updateRow implementation
- Arrays.fill(columnGotUpdated, false);
- currentRowHasBeenUpdated = false;
- }
-
return movePosition(NEXT, 0, "next");
}
@@ -384,6 +399,7 @@
setupContextStack();
try {
LanguageConnectionContext lcc = getEmbedConnection().getLanguageConnection();
+ final ExecRow newRow;
try {
/* Push and pop a StatementContext around a next call
@@ -401,38 +417,39 @@
switch (position)
{
case BEFOREFIRST:
- currentRow = theResults.setBeforeFirstRow();
+ newRow = theResults.setBeforeFirstRow();
break;
case FIRST:
- currentRow = theResults.getFirstRow();
+ newRow = theResults.getFirstRow();
break;
case NEXT:
- currentRow = theResults.getNextRow();
+ newRow = theResults.getNextRow();
break;
case LAST:
- currentRow = theResults.getLastRow();
+ newRow = theResults.getLastRow();
break;
case AFTERLAST:
- currentRow = theResults.setAfterLastRow();
+ newRow = theResults.setAfterLastRow();
break;
case PREVIOUS:
- currentRow = theResults.getPreviousRow();
+ newRow = theResults.getPreviousRow();
break;
case ABSOLUTE:
- currentRow = theResults.getAbsoluteRow(row);
+ newRow = theResults.getAbsoluteRow(row);
break;
case RELATIVE:
- currentRow = theResults.getRelativeRow(row);
+ newRow = theResults.getRelativeRow(row);
break;
default:
+ newRow = null;
if (SanityManager.DEBUG)
{
SanityManager.THROWASSERT(
@@ -458,8 +475,14 @@
else
topWarning.setNextWarning(w);
}
-
- boolean onRow = (currentRow!=null);
+
+ boolean onRow = (newRow!=null);
+ if (onRow) {
+ currentRow.setRowArray(newRow.getRowArray());
+ } else {
+ currentRow.setRowArray(null);
+ }
+
//if (onRow && !(currentRow instanceof org.apache.derby.impl.sql.execute.ValueRow))
// System.out.println(currentRow.getClass());
@@ -496,11 +519,12 @@
owningStmt.resultSetClosing(this);
}
- rowData = onRow ? currentRow.getRowArray() : null;
-
// Clear the indication of which columns were fetched as streams.
if (streamUsedFlags != null)
Arrays.fill(streamUsedFlags, false);
+ if (columnGotUpdated != null && currentRowHasBeenUpdated) {
+ initializeUpdateRowModifiers();
+ }
return onRow;
} finally {
@@ -595,8 +619,7 @@
}
// the idea is to release resources, so:
- currentRow = null;
- rowData = null;
+ currentRow.setRowArray(null);
rMetaData = null; // let it go, we can make a new one
// we hang on to theResults and messenger
@@ -2227,7 +2250,7 @@
checkUpdatableCursor(methodName);
//3)Make sure JDBC ResultSet is positioned on a row
- checkOnRow(); // first make sure there's a current row
+ if (!isOnInsertRow) checkOnRow(); // make sure there's a current row
//in case of autocommit on, if there was an exception which caused runtime rollback in this transaction prior to this call,
//the rollback code will mark the language resultset closed (it doesn't mark the JDBC ResultSet closed).
//That is why alongwith the earlier checkIfClosed call in this method, there is a check for language resultset close as well.
@@ -2240,14 +2263,10 @@
//mark the column as updated and return DataValueDescriptor for it. It will be used by updateXXX methods to put new values
protected DataValueDescriptor getDVDforColumnToBeUpdated(int columnIndex, String updateMethodName) throws StandardException, SQLException {
checksBeforeUpdateXXX(updateMethodName, columnIndex);
- if (columnGotUpdated[columnIndex-1] == false) {//this is the first updateXXX call on this column
- //this is the first updateXXX method call on this column. Save the original content of the column into copyOfDatabaseRow
- //The saved copy of the column will be needed if cancelRowUpdates is issued
- copyOfDatabaseRow[columnIndex - 1] = currentRow.getColumn(columnIndex).getClone();
- }
columnGotUpdated[columnIndex-1] = true;
- currentRowHasBeenUpdated = true;
- return currentRow.getColumn(columnIndex);
+ currentRowHasBeenUpdated = true;
+
+ return updateRow.getColumn(columnIndex);
}
/* do following few checks before accepting insertRow
@@ -2850,7 +2869,7 @@
throw newSQLException(SQLState.BAD_SCALE_VALUE, new Integer(scale));
try {
- DataValueDescriptor value = currentRow.getColumn(columnIndex);
+ DataValueDescriptor value = updateRow.getColumn(columnIndex);
int origvaluelen = value.getLength();
((VariableSizeDataValue)
@@ -3456,7 +3475,7 @@
if (columnGotUpdated[i-1]) {
act.getParameterValueSet().
getParameterForSet(paramPosition++).
- setValue(currentRow.getColumn(i));
+ setValue(updateRow.getColumn(i));
}
}
// Don't see any timeout when inserting rows (use 0)
@@ -3530,7 +3549,7 @@
//in this for loop we are assigning values for parameters in sql constructed earlier with columnname=?,...
for (int i=1, paramPosition=0; i<=rd.getColumnCount(); i++) {
if (columnGotUpdated[i-1]) //if the column got updated, do following
- act.getParameterValueSet().getParameterForSet(paramPosition++).setValue(currentRow.getColumn(i));
+ act.getParameterValueSet().getParameterForSet(paramPosition++).setValue(updateRow.getColumn(i));
}
// Don't set any timeout when updating rows (use 0)
org.apache.derby.iapi.sql.ResultSet rs = ps.execute(act, false, true, true, 0L); //execute the update where current of sql
@@ -3542,8 +3561,9 @@
rs.finish();
//For forward only resultsets, after a update, the ResultSet will be positioned right before the next row.
if (getType() == TYPE_FORWARD_ONLY) {
- rowData = null;
- currentRow = null;
+ currentRow.setRowArray(null);
+ } else {
+ movePosition(RELATIVE, 0, "relative");
}
lcc.popStatementContext(statementContext, null);
} catch (StandardException t) {
@@ -3552,6 +3572,7 @@
if (statementContext != null)
lcc.popStatementContext(statementContext, null);
restoreContextStack();
+ initializeUpdateRowModifiers();
}
}
}
@@ -3600,13 +3621,13 @@
rs.finish();
//After a delete, the ResultSet will be positioned right before
//the next row.
- rowData = null;
- currentRow = null;
+ currentRow.setRowArray(null);
lcc.popStatementContext(statementContext, null);
} catch (StandardException t) {
throw closeOnTransactionError(t);
} finally {
restoreContextStack();
+ initializeUpdateRowModifiers();
}
}
}
@@ -3663,17 +3684,9 @@
checksBeforeUpdateOrDelete("cancelRowUpdates", -1);
checkNotOnInsertRow();
-
- if (currentRowHasBeenUpdated == false) return; //nothing got updated on this row so cancelRowUpdates is a no-op in this case.
- for (int i=0; i < columnGotUpdated.length; i++){
- if (columnGotUpdated[i] == true) currentRow.setColumn(i+1, copyOfDatabaseRow[i]);//if column got updated, resotre the original data
- columnGotUpdated[i] = false;
- }
- currentRowHasBeenUpdated = false;
- //rowData needs to be refreshed with the currentRow otherwise it will continue to have changes made by updateXXX methods
- rowData = currentRow.getRowArray();
- }
+ initializeUpdateRowModifiers();
+ }
/**
* JDBC 2.0
@@ -3703,30 +3716,13 @@
synchronized (getConnectionSynchronization()) {
try {
// initialize state corresponding to insertRow/updateRow impl.
- for (int i=0; i < columnGotUpdated.length; i++) {
- columnGotUpdated[i] = false;
- }
- currentRowHasBeenUpdated = false;
-
- // Remember position
- if (!isOnInsertRow) {
- currentRowBeforeInsert = currentRow;
- }
-
- isOnInsertRow = true;
-
- // If insertRow has not been allocated yet, get new insertRow
- if (insertRow == null) {
- insertRow = stmt.lcc.getExecutionContext().
- getExecutionFactory().getValueRow(columnGotUpdated.length);
- }
+ initializeUpdateRowModifiers();
+ isOnInsertRow = true;
+
for (int i=1; i <= columnGotUpdated.length; i++) {
- insertRow.setColumn(i,
+ updateRow.setColumn(i,
resultDescription.getColumnDescriptor(i).getType().getNull());
}
- // Set currentRow to insertRow
- currentRow = insertRow;
- rowData = currentRow.getRowArray();
} catch (Throwable ex) {
handleException(ex);
}
@@ -3753,20 +3749,8 @@
try {
if (isOnInsertRow) {
- // Get position previous to moveToInsertRow
- currentRow = currentRowBeforeInsert;
- currentRowBeforeInsert = null;
-
// initialize state corresponding to insertRow/updateRow impl.
- for (int i=0; i < columnGotUpdated.length; i++) {
- columnGotUpdated[i] = false;
- }
- currentRowHasBeenUpdated = false;
-
- // Get rowData
- if (currentRow != null) {
- rowData = currentRow.getRowArray();
- }
+ initializeUpdateRowModifiers();
isOnInsertRow = false;
}
@@ -3803,7 +3787,7 @@
boolean pushStack = false;
try {
- DataValueDescriptor dvd = currentRow.getColumn(columnIndex);
+ DataValueDescriptor dvd = getColumn(columnIndex);
if (wasNull = dvd.isNull())
return null;
@@ -3855,7 +3839,7 @@
boolean pushStack = false;
try {
- DataValueDescriptor dvd = currentRow.getColumn(columnIndex);
+ DataValueDescriptor dvd = getColumn(columnIndex);
if (wasNull = dvd.isNull())
return null;
@@ -4201,16 +4185,16 @@
closeCurrentStream();
- DataValueDescriptor[] theCurrentRow = checkOnRow(); // first make sure there's a row
-
- try {
- return theCurrentRow[columnIndex - 1];
- } catch (ArrayIndexOutOfBoundsException aoobe) {
- throw newSQLException(SQLState.COLUMN_NOT_FOUND,
- new Integer(columnIndex));
- }
-
- // return theCurrentRow.getColumn(columnIndex);
+ if (columnIndex < 1 || columnIndex > currentRow.nColumns()) {
+ throw newSQLException(SQLState.COLUMN_NOT_FOUND,
+ new Integer(columnIndex));
+ }
+ if (isOnInsertRow || currentRowHasBeenUpdated && columnGotUpdated[columnIndex -1]) {
+ return updateRow.getColumn(columnIndex);
+ } else {
+ checkOnRow(); // make sure there's a row
+ return currentRow.getColumn(columnIndex);
+ }
}
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SURTest.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SURTest.java?rev=406279&r1=406278&r2=406279&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SURTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SURTest.java Sun May 14 00:54:58 2006
@@ -242,6 +242,219 @@
verifyTuple(rs);
assertFailOnUpdate(rs);
}
+
+ /**
+ * Test that you can correctly run multiple updateXXX() + updateRow()
+ * combined with cancelRowUpdates().
+ */
+ public void testMultiUpdateRow1()
+ throws SQLException
+ {
+ Statement s = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
+ ResultSet.CONCUR_UPDATABLE);
+ s.setCursorName(getNextCursorName());
+ ResultSet rs = s.executeQuery("select * from t1");
+ rs.absolute(5);
+ final int oldCol2 = rs.getInt(2);
+ final int newCol2 = -2222;
+ final int oldCol3 = rs.getInt(3);
+ final int newCol3 = -3333;
+
+ rs.updateInt(2, newCol2);
+ assertEquals("Expected the resultset to be updated after updateInt",
+ newCol2, rs.getInt(2));
+ rs.cancelRowUpdates();
+ assertEquals("Expected updateXXX to have no effect after cancelRowUpdated",
+ oldCol2, rs.getInt(2));
+ rs.updateInt(2, newCol2);
+ assertEquals("Expected the resultset to be updated after updateInt",
+ newCol2, rs.getInt(2));
+ assertTrue("Expected rs.rowUpdated() to be false before updateRow",
+ !rs.rowUpdated());
+ rs.updateRow();
+
+ assertTrue("Expected rs.rowUpdated() to be true after updateRow",
+ rs.rowUpdated());
+ assertEquals("Expected the resultset detect the updates of previous " +
+ "updateRow", newCol2, rs.getInt(2));
+
+ rs.updateInt(3, newCol3);
+
+ assertEquals("Expected the resultset to be updated after updateInt",
+ newCol3, rs.getInt(3));
+ assertEquals("Expected the resultset detect the updates of previous " +
+ "updateRow", newCol2, rs.getInt(2));
+
+ rs.cancelRowUpdates();
+
+ assertEquals("Expected updateXXX to have no effect after " +
+ "cancelRowUpdated", oldCol3, rs.getInt(3));
+ assertEquals("Expected the resultset detect the updates of previous " +
+ "updateRow after cancelRowUpdated", newCol2, rs.getInt(2));
+ rs.updateInt(3, newCol3);
+ rs.updateRow();
+ assertEquals("Expected the resultset to be updated after updateInt",
+ newCol3, rs.getInt(3));
+ rs.cancelRowUpdates();
+
+ assertEquals("Expected the resultset detect the updates of previous" +
+ "updateRow after cancelRowUpdates", newCol2, rs.getInt(2));
+ assertEquals("Expected the resultset detect the updates of previous" +
+ "updateRow after cancelRowUpdates", newCol3, rs.getInt(3));
+ assertTrue("Expected rs.rowUpdated() to be true after " +
+ "updateRow and cancelRowUpdates", rs.rowUpdated());
+
+ rs.close();
+ }
+
+ /**
+ * Test that you can correctly run multiple updateNull() + updateRow()
+ * combined with cancelRowUpdates().
+ */
+ public void testMultiUpdateRow2()
+ throws SQLException
+ {
+ Statement s = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
+ ResultSet.CONCUR_UPDATABLE);
+ s.setCursorName(getNextCursorName());
+ ResultSet rs = s.executeQuery("select * from t1");
+ rs.absolute(5);
+ final int oldCol2 = rs.getInt(2);
+ final int oldCol3 = rs.getInt(3);
+
+ rs.updateNull(2);
+ assertEquals("Expected the resultset to be updated after updateNull",
+ 0, rs.getInt(2));
+ assertTrue("Expected wasNull to be true after updateNull", rs.wasNull());
+ rs.cancelRowUpdates();
+ assertEquals("Expected updateXXX to have no effect after cancelRowUpdated",
+ oldCol2, rs.getInt(2));
+ rs.updateNull(2);
+ assertEquals("Expected the resultset to be updated after updateNull",
+ 0, rs.getInt(2));
+ assertTrue("Expected wasNull to be true after updateNull", rs.wasNull());
+ assertTrue("Expected rs.rowUpdated() to be false before updateRow",
+ !rs.rowUpdated());
+ rs.updateRow();
+
+ assertTrue("Expected rs.rowUpdated() to be true after updateRow",
+ rs.rowUpdated());
+ assertEquals("Expected the resultset detect the updates of previous " +
+ "updateRow", 0, rs.getInt(2));
+
+ rs.updateNull(3);
+
+ assertEquals("Expected the resultset to be updated after updateNull",
+ 0, rs.getInt(3));
+ assertTrue("Expected wasNull to be true after updateNull", rs.wasNull());
+ assertEquals("Expected the resultset detect the updates of previous " +
+ "updateRow", 0, rs.getInt(2));
+
+ rs.cancelRowUpdates();
+
+ assertEquals("Expected updateXXX to have no effect after " +
+ "cancelRowUpdated", oldCol3, rs.getInt(3));
+ assertEquals("Expected the resultset detect the updates of previous " +
+ "updateRow after cancelRowUpdated", 0, rs.getInt(2));
+ rs.updateNull(3);
+ rs.updateRow();
+ assertEquals("Expected the resultset to be updated after updateNull",
+ 0, rs.getInt(3));
+ rs.cancelRowUpdates();
+
+ assertEquals("Expected the resultset detect the updates of previous" +
+ "updateRow after cancelRowUpdates", 0, rs.getInt(2));
+ assertEquals("Expected the resultset detect the updates of previous" +
+ "updateRow after cancelRowUpdates", 0, rs.getInt(3));
+ assertTrue("Expected rs.rowUpdated() to be true after " +
+ "updateRow and cancelRowUpdates", rs.rowUpdated());
+
+ rs.close();
+ }
+
+ /**
+ * Test that you get cursor operation conflict warning if updating
+ * a row which has been deleted from the table.
+ */
+ public void testCursorOperationConflictWarning1()
+ throws SQLException
+ {
+ Statement s = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
+ ResultSet.CONCUR_UPDATABLE);
+ s.setCursorName(getNextCursorName());
+ ResultSet rs = s.executeQuery("select * from t1");
+ rs.next();
+ con.createStatement().executeUpdate("delete from t1 where id=" +
+ rs.getString("ID"));
+ final int newValue = -3333;
+ final int oldValue = rs.getInt(2);
+ rs.updateInt(2, newValue);
+ rs.updateRow();
+
+ SQLWarning warn = rs.getWarnings();
+ assertWarning(warn, CURSOR_OPERATION_CONFLICT);
+ assertEquals("Did not expect the resultset to be updated", oldValue, rs.getInt(2));
+ assertTrue("Expected rs.rowDeleted() to be false", !rs.rowDeleted());
+ assertTrue("Expected rs.rowUpdated() to be false", !rs.rowUpdated());
+
+ rs.clearWarnings();
+ rs.deleteRow();
+ warn = rs.getWarnings();
+ assertWarning(warn, CURSOR_OPERATION_CONFLICT);
+ rs.relative(0);
+ assertTrue("Expected rs.rowUpdated() to be false", !rs.rowUpdated());
+ assertTrue("Expected rs.rowDeleted() to be false", !rs.rowDeleted());
+ assertEquals("Did not expect the resultset to be updated", oldValue, rs.getInt(2));
+
+ rs.close();
+ }
+
+ /**
+ * Test that you get cursor operation conflict warning if updating
+ * a row which has been deleted from the table, now using
+ * positioned updates / deletes.
+ */
+ public void testCursorOperationConflictWarning2()
+ throws SQLException
+ {
+ Statement s = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
+ ResultSet.CONCUR_UPDATABLE);
+ s.setCursorName(getNextCursorName());
+ ResultSet rs = s.executeQuery("select * from t1");
+ rs.next();
+ con.createStatement().executeUpdate ("delete from t1 where id=" +
+ rs.getString("ID"));
+
+ final int newValue = -3333;
+ final int oldValue = rs.getInt(2);
+
+ Statement s3 = con.createStatement();
+ int updateCount = s3.executeUpdate
+ ("update t1 set A=" + newValue +
+ " where current of " + rs.getCursorName());
+
+ rs.relative(0);
+ SQLWarning warn = s3.getWarnings();
+ assertWarning(warn, CURSOR_OPERATION_CONFLICT);
+ assertTrue("Expected rs.rowUpdated() to be false", !rs.rowUpdated());
+ assertTrue("Expected rs.rowDeleted() to be false", !rs.rowDeleted());
+ assertEquals("Did not expect the resultset to be updated", oldValue, rs.getInt(2));
+ assertEquals("Expected update count to be 0", 0, updateCount);
+
+ Statement s4 = con.createStatement();
+ updateCount = s4.executeUpdate("delete from t1 where current of " +
+ rs.getCursorName());
+
+ rs.relative(0);
+ warn = s4.getWarnings();
+ assertWarning(warn, CURSOR_OPERATION_CONFLICT);
+ assertTrue("Expected rs.rowUpdated() to be false", !rs.rowUpdated());
+ assertTrue("Expected rs.rowDeleted() to be false", !rs.rowDeleted());
+ assertEquals("Did not expect the resultset to be updated", oldValue, rs.getInt(2));
+ assertEquals("Expected update count to be 0", 0, updateCount);
+
+ rs.close();
+ }
/**
* Test that you can scroll forward and update indexed records in