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 da...@apache.org on 2009/06/16 13:15:40 UTC
svn commit: r785163 - in /db/derby/code/trunk/java:
engine/org/apache/derby/iapi/services/io/
engine/org/apache/derby/iapi/sql/execute/
engine/org/apache/derby/impl/sql/execute/
testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/
Author: dag
Date: Tue Jun 16 11:15:39 2009
New Revision: 785163
URL: http://svn.apache.org/viewvc?rev=785163&view=rev
Log:
DERBY-4198 When using the FOR UPDATE OF clause with SUR (Scroll-insensive updatable result sets), the updateRow() method crashes
Patch derby-4198-4. This is the second part of the solution for this
issue. It solves the issue of column mapping in the presence of named
columns in a FOR UPDATE OF clause. The original code was not general
enough; ScrollInsensitiveResultSet.updateRow needs to make use of
RowChanger to do the right thing.
The patch also adds new test cases.
Modified:
db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/FormatableBitSet.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/NoPutResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/RowChanger.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CurrentOfResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoPutResultSetImpl.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NormalizeResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RowChangerImpl.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ScrollInsensitiveResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderResultSet.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SURTest.java
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/FormatableBitSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/FormatableBitSet.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/FormatableBitSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/io/FormatableBitSet.java Tue Jun 16 11:15:39 2009
@@ -685,7 +685,9 @@
* By using anySetBit() and anySetBit(beyondBit), one can quickly go
* thru the entire bit array to return all set bit.
*
- * @param beyondBit only look at bit that is greater than this bit number
+ * @param beyondBit Only look at bit that is greater than this bit number.
+ * Supplying a value of -1 makes the call equivalent to
+ * anySetBit().
* @return the bit number of a bit that is set, or -1 if no bit after
* beyondBit is set
*/
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/NoPutResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/NoPutResultSet.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/NoPutResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/NoPutResultSet.java Tue Jun 16 11:15:39 2009
@@ -24,6 +24,7 @@
import org.apache.derby.iapi.sql.ResultSet;
import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.sql.execute.RowChanger;
import org.apache.derby.iapi.types.RowLocation;
import org.apache.derby.iapi.store.access.RowLocationRetRowSource;
@@ -181,10 +182,14 @@
* JDBC's udpateRow method.
*
* @param row new values for the currentRow
+ * @param rowChanger holds information about row: what columns of it is to
+ * be used for updating, and what underlying base table column each
+ * such column corresponds to.
*
* @exception StandardException thrown on failure.
*/
- public void updateRow(ExecRow row) throws StandardException;
+ public void updateRow(ExecRow row, RowChanger rowChanger)
+ throws StandardException;
/**
* Marks the resultSet's currentRow as deleted after a delete has been
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/RowChanger.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/RowChanger.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/RowChanger.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/RowChanger.java Tue Jun 16 11:15:39 2009
@@ -23,6 +23,7 @@
import org.apache.derby.iapi.services.context.ContextService;
import org.apache.derby.iapi.services.sanity.SanityManager;
+import org.apache.derby.iapi.services.io.FormatableBitSet;
import org.apache.derby.iapi.error.StandardException;
@@ -161,4 +162,15 @@
*/
public void open(int lockMode, boolean wait)
throws StandardException;
+
+ /**
+ * Return what column no in the input ExecRow (cf nextBaseRow argument to
+ * #updateRow) would correspond to selected column, if any.
+ *
+ * @param selectedCol the column number in the base table of a selected
+ * column or -1 (if selected column is not a base table
+ * column, e.g. i+4).
+ * @returns column no, or -1 if not found or not a base column
+ */
+ public int findSelectedCol(int selectedCol);
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CurrentOfResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CurrentOfResultSet.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CurrentOfResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/CurrentOfResultSet.java Tue Jun 16 11:15:39 2009
@@ -39,6 +39,7 @@
import org.apache.derby.iapi.services.sanity.SanityManager;
import org.apache.derby.iapi.sql.depend.DependencyManager;
+import org.apache.derby.iapi.sql.execute.RowChanger;
/**
* Takes a cursor name and returns the current row
@@ -330,8 +331,9 @@
/**
* @see NoPutResultSet#updateRow
*/
- public void updateRow (ExecRow row) throws StandardException {
- ((NoPutResultSet)cursor).updateRow(row);
+ public void updateRow (ExecRow row, RowChanger rowChanger)
+ throws StandardException {
+ ((NoPutResultSet)cursor).updateRow(row, rowChanger);
}
/**
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoPutResultSetImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoPutResultSetImpl.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoPutResultSetImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoPutResultSetImpl.java Tue Jun 16 11:15:39 2009
@@ -39,6 +39,7 @@
import org.apache.derby.iapi.sql.execute.xplain.XPLAINVisitor;
import org.apache.derby.iapi.sql.execute.NoPutResultSet;
import org.apache.derby.iapi.sql.execute.TargetResultSet;
+import org.apache.derby.iapi.sql.execute.RowChanger;
import org.apache.derby.iapi.store.access.Qualifier;
import org.apache.derby.iapi.store.access.RowLocationRetRowSource;
import org.apache.derby.iapi.store.access.RowSource;
@@ -512,7 +513,8 @@
* This method is result sets used for scroll insensitive updatable
* result sets for other result set it is a no-op.
*/
- public void updateRow(ExecRow row) throws StandardException {
+ public void updateRow(ExecRow row, RowChanger rowChanger)
+ throws StandardException {
// Only ResultSets of type Scroll Insensitive implement
// detectability, so for other result sets this method
// is a no-op
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NormalizeResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NormalizeResultSet.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NormalizeResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NormalizeResultSet.java Tue Jun 16 11:15:39 2009
@@ -30,6 +30,7 @@
import org.apache.derby.iapi.sql.execute.CursorResultSet;
import org.apache.derby.iapi.sql.execute.ExecRow;
import org.apache.derby.iapi.sql.execute.NoPutResultSet;
+import org.apache.derby.iapi.sql.execute.RowChanger;
import org.apache.derby.iapi.types.DataTypeDescriptor;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.RowLocation;
@@ -403,8 +404,9 @@
/**
* @see NoPutResultSet#updateRow
*/
- public void updateRow (ExecRow row) throws StandardException {
- source.updateRow(row);
+ public void updateRow (ExecRow row, RowChanger rowChanger)
+ throws StandardException {
+ source.updateRow(row, rowChanger);
}
/**
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ProjectRestrictResultSet.java Tue Jun 16 11:15:39 2009
@@ -46,6 +46,7 @@
import org.apache.derby.iapi.types.RowLocation;
import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
+import org.apache.derby.iapi.sql.execute.RowChanger;
/**
@@ -583,8 +584,9 @@
/**
* @see NoPutResultSet#updateRow
*/
- public void updateRow (ExecRow row) throws StandardException {
- source.updateRow(row);
+ public void updateRow (ExecRow row, RowChanger rowChanger)
+ throws StandardException {
+ source.updateRow(row, rowChanger);
}
/**
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RowChangerImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RowChangerImpl.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RowChangerImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/RowChangerImpl.java Tue Jun 16 11:15:39 2009
@@ -593,4 +593,52 @@
java.util.Arrays.sort(output);
return output;
}
+
+
+ public int findSelectedCol(int selectedCol) {
+ if (selectedCol == -1) {
+ // This is not a base column
+ return -1;
+ }
+
+ int[] changeColArray = (partialChangedColumnIds == null) ?
+ changedColumnIds : partialChangedColumnIds;
+
+ int nextColumnToUpdate = -1;
+ for (int i = 0; i < changeColArray.length; i++) {
+ nextColumnToUpdate =
+ changedColumnBitSet.anySetBit(nextColumnToUpdate);
+
+ if (selectedCol == nextColumnToUpdate + 1) { // bit set is 0 based
+ return changeColArray[i];
+ }
+ }
+
+ return -1;
+ }
+
+
+ public String toString() {
+ if (SanityManager.DEBUG) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("changedColumnBitSet: " + changedColumnBitSet + "\n");
+
+ int[] changedColumnArray = (partialChangedColumnIds == null) ?
+ changedColumnIds : partialChangedColumnIds;
+
+ sb.append("changedColumnArray: [");
+ for (int i = 0; i < changedColumnArray.length; i++) {
+ sb.append(changedColumnArray[i]);
+
+ if (i < changedColumnArray.length-1) {
+ sb.append(",");
+ }
+ }
+ sb.append("]");
+
+ return sb.toString();
+ } else {
+ return super.toString();
+ }
+ }
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ScrollInsensitiveResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ScrollInsensitiveResultSet.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ScrollInsensitiveResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/ScrollInsensitiveResultSet.java Tue Jun 16 11:15:39 2009
@@ -37,6 +37,7 @@
import org.apache.derby.iapi.store.access.BackingStoreHashtable;
+import org.apache.derby.iapi.sql.execute.RowChanger;
import org.apache.derby.iapi.types.SQLBoolean;
import org.apache.derby.iapi.types.SQLInteger;
@@ -1092,15 +1093,15 @@
}
}
+
/**
* @see NoPutResultSet#updateRow
*
* Sets the updated column of the hash table to true and updates the row
* in the hash table with the new values for the row.
*/
- public void updateRow(ExecRow row) throws StandardException {
- ExecRow newRow = row;
- boolean undoProjection = false;
+ public void updateRow(ExecRow row, RowChanger rowChanger)
+ throws StandardException {
ProjectRestrictResultSet prRS = null;
@@ -1112,41 +1113,72 @@
prRS = ((RowCountResultSet)source).getUnderlyingProjectRestrictRS();
}
- if (prRS != null) {
- newRow = prRS.doBaseRowProjection(row);
- undoProjection = true;
- }
-
positionInHashTable.setValue(currentPosition);
- DataValueDescriptor[] hashRowArray = (DataValueDescriptor[])
+ DataValueDescriptor[] hashRowArray = (DataValueDescriptor[])
ht.get(positionInHashTable);
RowLocation rowLoc = (RowLocation) hashRowArray[POS_ROWLOCATION];
+
+ // Maps from each selected column to underlying base table column
+ // number, i.e. as from getBaseProjectMapping if a PRN exists, if not
+ // we construct one, so we always know where in the hash table a
+ // modified column will need to go (we do our own projection).
+ int[] map;
+
+ if (prRS != null) {
+ map = prRS.getBaseProjectMapping();
+ } else {
+ // create a natural projection mapping for all columns in SELECT
+ // list so we can treat the cases of no PRN and PRN the same.
+ int noOfSelectedColumns =
+ hashRowArray.length - (LAST_EXTRA_COLUMN+1);
+
+ map = new int[noOfSelectedColumns];
+
+ // initialize as 1,2,3, .. n which we know is correct since there
+ // is no underlying PRN.
+ for (int i=0; i < noOfSelectedColumns; i++) {
+ map[i] = i+1; // column is 1-based
+ }
+ }
+
+ // Construct a new row based on the old one and the updated columns
+ ExecRow newRow = new ValueRow(map.length);
+
+ for (int i=0; i < map.length; i++) {
+ // What index in ExecRow "row" corresponds to this position in the
+ // hash table, if any?
+ int rowColumn = rowChanger.findSelectedCol(map[i]);
+
+ if (rowColumn > 0) {
+ // OK, a new value has been supplied, use it
+ newRow.setColumn(i+1, row.getColumn(rowColumn));
+ } else {
+ // No new value, so continue using old one
+ newRow.setColumn(i+1, hashRowArray[LAST_EXTRA_COLUMN + 1 + i]);
+ }
+ }
+
ht.remove(new SQLInteger(currentPosition));
addRowToHashTable(newRow, currentPosition, rowLoc, true);
-
+
// Modify row to refer to data in the BackingStoreHashtable.
// This allows reading of data which goes over multiple pages
// when doing the actual update (LOBs). Putting columns of
// type SQLBinary to disk, has destructive effect on the columns,
// and they need to be re-read. That is the reason this is needed.
- if (undoProjection) {
-
- final DataValueDescriptor[] newRowData = newRow.getRowArray();
-
- // Array of original position in row
- final int[] origPos = prRS.getBaseProjectMapping();
-
- // We want the row to contain data backed in BackingStoreHashtable
- final DataValueDescriptor[] backedData =
- getRowArrayFromHashTable(currentPosition);
-
- for (int i=0; i<origPos.length; i++) {
- if (origPos[i]>=0) {
- row.setColumn(origPos[i], backedData[i]);
- }
+
+ DataValueDescriptor[] backedData =
+ getRowArrayFromHashTable(currentPosition);
+
+ for (int i=0; i < map.length; i++) {
+ // What index in "row" corresponds to this position in the table,
+ // if any?
+ int rowColumn = rowChanger.findSelectedCol(map[i]);
+
+ if (rowColumn > 0) {
+ // OK, put the value in the hash table back to row.
+ row.setColumn(rowColumn, backedData[i]);
}
- } else {
- row.setRowArray(getRowArrayFromHashTable(currentPosition));
}
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderResultSet.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderResultSet.java Tue Jun 16 11:15:39 2009
@@ -33,6 +33,7 @@
import org.apache.derby.iapi.sql.execute.CursorResultSet;
import org.apache.derby.iapi.sql.execute.ExecRow;
import org.apache.derby.iapi.sql.execute.NoPutResultSet;
+import org.apache.derby.iapi.sql.execute.RowChanger;
import org.apache.derby.iapi.sql.execute.TargetResultSet;
import org.apache.derby.iapi.store.access.ConglomerateController;
import org.apache.derby.iapi.store.access.ScanController;
@@ -1124,7 +1125,8 @@
* This method is result sets used for scroll insensitive updatable
* result sets for other result set it is a no-op.
*/
- public void updateRow(ExecRow row) throws StandardException {
+ public void updateRow(ExecRow row, RowChanger rowChanger)
+ throws StandardException {
// Only ResultSets of type Scroll Insensitive implement
// detectability, so for other result sets this method
// is a no-op
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java?rev=785163&r1=785162&r2=785163&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java Tue Jun 16 11:15:39 2009
@@ -557,7 +557,7 @@
riChecker.doFKCheck(newBaseRow);
}
- source.updateRow(newBaseRow);
+ source.updateRow(newBaseRow, rowChanger);
rowChanger.updateRow(row,newBaseRow,baseRowLocation);
//beetle 3865, update cursor use index.
@@ -885,7 +885,6 @@
rowChanger.updateRow(deferredBaseRow,
newBaseRow,
baseRowLocation);
- source.updateRow(newBaseRow);
}
} finally
{
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SURTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SURTest.java?rev=785163&r1=785162&r2=785163&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 Tue Jun 16 11:15:39 2009
@@ -1394,6 +1394,165 @@
}
/**
+ * DERBY-4198 "When using the FOR UPDATE OF clause with SUR
+ * (Scroll-insensive updatable result sets), the updateRow() method crashes"
+ *
+ * This bug revealed missing logic to handle the fact the the ExecRow
+ * passed down to ScrollInsensitiveResultSet.updateRow does not always
+ * contain all the rows of the basetable, cf. the logic of RowChangerImpl.
+ * When an explicit list of columns is given as in FOR UPDATE OF
+ * <column-list>, the ExecRow may contains a subset of the the base table
+ * columns and ScrollInsensitiveResultSet was not ready to handle that.
+ *
+ * Test some of the cases which went wrong before the fix.
+ *
+ */
+ public void testForUpdateWithColumnList() throws SQLException {
+ Statement s = createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
+ ResultSet.CONCUR_UPDATABLE);
+
+ // case a)
+ ResultSet rs = s.executeQuery("select c from t1 for update of c");
+
+ rs.next();
+ rs.updateString(1,"foobar");
+ rs.updateRow();
+ rs.next();
+ rs.previous();
+ assertEquals("foobar", rs.getString(1));
+ rs.close();
+
+ // case b)
+ rs = s.executeQuery("select id from t1 for update of id");
+ rs.next();
+ rs.updateInt(1,20);
+ rs.updateRow();
+ rs.next();
+ rs.previous();
+ assertEquals(20, rs.getInt(1));
+ rs.close();
+
+ // case c)
+ rs = s.executeQuery("select * from t1 for update of id");
+ rs.next();
+ rs.updateInt(1,20);
+ rs.updateRow();
+ rs.next();
+ rs.previous();
+ assertEquals(20, rs.getInt(1));
+ rs.close();
+
+ // case d)
+ rs = s.executeQuery("SELECT * from t1 for update of c");
+ rs.next();
+ int id = rs.getInt(1);
+ int a = rs.getInt(2);
+ int b = rs.getInt(3);
+ rs.updateString(4,"foobar");
+ rs.updateRow();
+ rs.next();
+ rs.previous();
+ assertEquals(id, rs.getInt(1));
+ assertEquals(a, rs.getInt(2));
+ assertEquals(b, rs.getInt(3));
+ assertEquals("foobar", rs.getString(4));
+ rs.close();
+
+ // case e)
+ rs = s.executeQuery("SELECT * from t1 for update of id,a,b,c");
+ rs.next();
+ rs.updateInt(1, -20);
+ rs.updateInt(2, 20);
+ rs.updateInt(3, 21);
+ rs.updateString(4,"foobar");
+ rs.updateRow();
+ rs.next();
+ rs.previous();
+ assertEquals(-20, rs.getInt(1));
+ assertEquals(20, rs.getInt(2));
+ assertEquals(21, rs.getInt(3));
+ assertEquals("foobar", rs.getString(4));
+ rs.close();
+
+ // case f)
+ rs = s.executeQuery("SELECT * from t1 for update of id, a,b,c");
+ rs.next();
+ rs.updateInt(1, 20);
+ rs.updateRow();
+ rs.next();
+ rs.previous();
+ assertEquals(20, rs.getInt(1));
+ rs.close();
+
+ // case h)
+ rs = s.executeQuery("SELECT id from t1 for update of id, c");
+ String cursorname = rs.getCursorName();
+ rs.next();
+ Statement s2 = createStatement();
+ s2.executeUpdate("update t1 set c='foobar' where current of " +
+ cursorname);
+ s2.close();
+ rs.next();
+ rs.previous();
+ rs.getInt(1); // gives error state 22018 before fix
+ rs.close();
+
+ // case i)
+ rs = s.executeQuery("SELECT id from t1 for update");
+ cursorname = rs.getCursorName();
+ rs.next();
+ s2 = createStatement();
+ s2.executeUpdate("update t1 set c='foobar' where current of " +
+ cursorname);
+ s2.close();
+ rs.next();
+ rs.previous();
+ rs.getInt(1); // ok before fix
+ rs.close();
+
+ // Odd cases: base row mentioned twice in rs, update 1st instance
+ rs = s.executeQuery("SELECT id,a,id from t1");
+ rs.next();
+ rs.updateInt(1, 20);
+ rs.updateRow();
+ rs.next();
+ rs.previous();
+ assertEquals(20, rs.getInt(1));
+ assertEquals(20, rs.getInt(3));
+ rs.close();
+
+ // Odd cases: base row mentioned twice in rs, update 2nd instance
+ // with explicit column list; fails, see DERBY-4226.
+ rs = s.executeQuery("SELECT id,a,id from t1 for update of id");
+ rs.next();
+ try {
+ rs.updateInt(3, 20);
+ fail("should fail");
+ } catch (SQLException e) {
+ String sqlState = usingEmbedded() ? "42X31" : "XJ124";
+ assertSQLState(sqlState, e);
+ }
+ rs.close();
+
+ // Odd cases: base row mentioned twice in rs, update 2nd instance
+ // without explicit column list; works
+ rs = s.executeQuery("SELECT id,a,id from t1 for update");
+ rs.next();
+ rs.updateInt(3, 20);
+ rs.updateRow();
+ assertEquals(20, rs.getInt(1));
+ assertEquals(20, rs.getInt(3));
+ rs.next();
+ rs.previous();
+ assertEquals(20, rs.getInt(1));
+ assertEquals(20, rs.getInt(3));
+ rs.close();
+
+ s.close();
+ }
+
+
+ /**
* Check that detectability methods throw the correct exception
* when called in an illegal row state, that is, somehow not
* positioned on a row. Minion of testDetectabilityExceptions.
@@ -1569,6 +1728,7 @@
s.close();
}
+
/**
* Get a cursor name. We use the same cursor name for all cursors.
*/