db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kahat...@apache.org
Subject 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
Date Sun, 14 May 2006 07:54:58 GMT
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



Mime
View raw message