Return-Path: Delivered-To: apmail-db-derby-commits-archive@www.apache.org Received: (qmail 99422 invoked from network); 14 May 2006 07:55:22 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 14 May 2006 07:55:22 -0000 Received: (qmail 54335 invoked by uid 500); 14 May 2006 07:55:22 -0000 Delivered-To: apmail-db-derby-commits-archive@db.apache.org Received: (qmail 54304 invoked by uid 500); 14 May 2006 07:55:21 -0000 Mailing-List: contact derby-commits-help@db.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: "Derby Development" List-Id: Delivered-To: mailing list derby-commits@db.apache.org Received: (qmail 54292 invoked by uid 99); 14 May 2006 07:55:21 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 14 May 2006 00:55:21 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Sun, 14 May 2006 00:55:20 -0700 Received: (qmail 99368 invoked by uid 65534); 14 May 2006 07:54:59 -0000 Message-ID: <20060514075459.99367.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit 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 -0000 To: derby-commits@db.apache.org From: kahatlen@apache.org X-Mailer: svnmailer-1.0.8 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N 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