blob: fc7bfda9ccf63dc2f443b0124538b413e8a01acb [file] [log] [blame]
/*
*
* Derby - Class ConcurrencyTest
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
package org.apache.derbyTesting.functionTests.tests.jdbcapi;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.TestConfiguration;
/**
* Testing concurrency behaviour in derby when creating the resultsets with
* different parameters.
*/
public class ConcurrencyTest extends SURBaseTest {
/** Creates a new instance of ConcurrencyTest */
public ConcurrencyTest(String name) {
super(name);
}
/**
* Sets up the connection, then create the data model
*/
public void setUp()
throws Exception
{
// For the concurrency tests, we recreate the model
// for each testcase (since we do commits)
SURDataModelSetup.createDataModel
(SURDataModelSetup.SURDataModel.MODEL_WITH_PK, getConnection());
commit();
}
public void tearDown() throws Exception
{
rollback();
dropTable("T1");
commit();
super.tearDown();
}
/**
* Test that update locks are downgraded to shared locks
* after repositioning.
* This test fails with Derby
*/
public void testUpdateLockDownGrade1()
throws SQLException
{
Statement s = createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1 for update");
// After navigating through the resultset,
// presumably all rows are locked with shared locks
while (rs.next());
// Now open up a connection
Connection con2 = openDefaultConnection();
Statement s2 = con2.createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs2 = s2.executeQuery("select * from t1 for update");
try {
rs2.next(); // We should be able to get a update lock here.
} catch (SQLException e) {
assertEquals("Unexpected SQL state", LOCK_TIMEOUT_SQL_STATE,
e.getSQLState());
return;
} finally {
con2.rollback();
}
assertTrue("Expected Derby to hold updatelocks in RR mode", false);
s2.close();
con2.close();
s.close();
}
/**
* Test that we can aquire a update lock even if the row is locked with
* a shared lock.
*/
public void testAquireUpdateLock1()
throws SQLException
{
Statement s = createStatement();
ResultSet rs = s.executeQuery("select * from t1");
// After navigating through the resultset,
// presumably all rows are locked with shared locks
while (rs.next());
// Now open up a connection
Connection con2 = openDefaultConnection();
Statement s2 = con2.createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs2 = s2.executeQuery("select * from t1 for update");
try {
rs2.next(); // We should be able to get a update lock here.
} finally {
con2.rollback();
}
s2.close();
con2.close();
s.close();
}
/*
* Test that we do not get a concurrency problem when opening two cursors
* as readonly.
**/
public void testSharedLocks1()
throws SQLException
{
Statement s = createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
final ResultSet rs = s.executeQuery("select * from t1");
scrollForward(rs);
Connection con2 = openDefaultConnection();
Statement s2 = con2.createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
try {
final ResultSet rs2 = s2.executeQuery("select * from t1");
scrollForward(rs2);
} finally {
rs.close();
con2.rollback();
con2.close();
}
s.close();
}
/*
* Test that we do not get a concurrency problem when opening two cursors
* reading the same data (no parameters specified to create statement).
**/
public void testSharedLocks2()
throws SQLException
{
Statement s = createStatement();
ResultSet rs = s.executeQuery("select * from t1");
scrollForward(rs);
Connection con2 = openDefaultConnection();
Statement s2 = con2.createStatement();
try {
final ResultSet rs2 = s2.executeQuery("select * from t1");
scrollForward(rs2);
} finally {
rs.close();
con2.rollback();
con2.close();
}
s.close();
}
/*
* Test that we do not get a concurrency problem when opening one cursor
* as updatable (not using "for update"), and another cursor as read only
**/
public void testSharedAndUpdateLocks1()
throws SQLException {
Statement s = createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1");
scrollForward(rs);
Connection con2 = openDefaultConnection();
Statement s2 = con2.createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
try {
final ResultSet rs2 = s2.executeQuery("select * from t1");
scrollForward(rs2);
} finally {
rs.close();
con2.rollback();
con2.close();
}
s.close();
}
/*
* Test that we do no get a concurrency problem when opening one cursor
* as updatable (using "for update"), and another cursor as read only.
*
**/
public void testSharedAndUpdateLocks2()
throws SQLException
{
Statement s = createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1 for update");
scrollForward(rs);
Connection con2 = openDefaultConnection();
Statement s2 = con2.createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
try {
final ResultSet rs2 = s2.executeQuery("select * from t1");
scrollForward(rs2);
} finally {
rs.close();
con2.rollback();
con2.close();
}
s.close();
}
/**
* Test what happens if you update a deleted + purged tuple.
* The transaction which deletes the tuple, will also
* ensure that the tuple is purged from the table, not only marked
* as deleted.
**/
public void testUpdatePurgedTuple1()
throws SQLException
{
getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
Statement s = createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1");
rs.next();
int firstKey = rs.getInt(1);
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
int lastKey = firstKey;
while (rs.next()) {
lastKey = rs.getInt(1);
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
}
Connection con2 = openDefaultConnection();
con2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
try {
PreparedStatement ps2 = con2.prepareStatement
("delete from t1 where id=? or id=?");
ps2.setInt(1, firstKey);
ps2.setInt(2, lastKey);
assertEquals("Expected two records to be deleted",
2, ps2.executeUpdate());
println("T2: Deleted records with id=" + firstKey + " and id=" +
lastKey);
con2.commit();
println("T2: commit");
ps2 = con2.prepareStatement
("call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE(?,?,?,?,?)");
ps2.setString(1, "APP"); // schema
ps2.setString(2, "T1"); // table name
ps2.setInt(3, 1); // purge
ps2.setInt(4, 0); // defragment rows
ps2.setInt(5, 0); // truncate end
println("T3: call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE");
println("T3: purges deleted records");
ps2.executeUpdate();
con2.commit();
println("T3: commit");
} catch (SQLException e) {
con2.rollback();
throw e;
}
rs.first(); // Go to first tuple
println("T1: Read first Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
rs.updateInt(2, 3);
println("T1: updateInt(2, 3);");
rs.updateRow();
println("T1: updateRow()");
rs.last(); // Go to last tuple
println("T1: Read last Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
rs.updateInt(2, 3);
println("T1: updateInt(2, 3);");
rs.updateRow();
println("T1: updateRow()");
commit();
println("T1: commit");
rs = s.executeQuery("select * from t1");
println("T3: select * from table");
while (rs.next()) {
println("T3: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
}
con2.close();
s.close();
}
/**
* Test what happens if you update a deleted tuple using positioned update
* (same as testUpdatePurgedTuple1, except here we use positioned updates)
**/
public void testUpdatePurgedTuple2()
throws SQLException
{
getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
Statement s = createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1");
rs.next(); // Point to first tuple
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
int firstKey = rs.getInt(1);
rs.next(); // Go to next
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
Connection con2 = openDefaultConnection();
con2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
try {
PreparedStatement ps2 = con2.prepareStatement
("delete from t1 where id=?");
ps2.setInt(1, firstKey);
assertEquals("Expected one record to be deleted", 1,
ps2.executeUpdate());
println("T2: Deleted record with id=" + firstKey);
con2.commit();
println("T2: commit");
} catch (SQLException e) {
con2.rollback();
throw e;
}
rs.previous(); // Go back to first tuple
println("T1: Read previous Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
PreparedStatement ps = prepareStatement
("update T1 set a=? where current of " + rs.getCursorName());
ps.setInt(1, 3);
int updateCount = ps.executeUpdate();
println("T1: update table, set a=3 where current of " +
rs.getCursorName());
println("T1: commit");
commit();
rs = s.executeQuery("select * from t1");
while (rs.next()) {
println("T3: Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
}
con2.close();
}
/**
* Test what happens if you update a tuple which is deleted, purged and
* reinserted
**/
public void testUpdatePurgedTuple3()
throws SQLException
{
getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
Statement s = createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1");
rs.next(); // Point to first tuple
int firstKey = rs.getInt(1);
println("T1: read tuple with key " + firstKey);
rs.next(); // Go to next
println("T1: read next tuple");
Connection con2 = openDefaultConnection();
con2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
try {
PreparedStatement ps2 = con2.prepareStatement
("delete from t1 where id=?");
ps2.setInt(1, firstKey);
assertEquals("Expected one record to be deleted", 1,
ps2.executeUpdate());
println("T2: Deleted record with id=" + firstKey);
con2.commit();
println("T2: commit");
// Now purge the table
ps2 = con2.prepareStatement
("call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE(?,?,?,?,?)");
ps2.setString(1, "APP"); // schema
ps2.setString(2, "T1"); // table name
ps2.setInt(3, 1); // purge
ps2.setInt(4, 0); // defragment rows
ps2.setInt(5, 0); // truncate end
println("T3: call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE");
println("T3: purges deleted records");
ps2.executeUpdate();
con2.commit();
ps2 = con2.prepareStatement("insert into t1 values(?,?,?,?,?)");
ps2.setInt(1, firstKey);
ps2.setInt(2, -1);
ps2.setInt(3, -1);
ps2.setString(4, "UPDATED TUPLE");
ps2.setString(5, "UPDATED CLOB");
assertEquals("Expected one record to be inserted", 1,
ps2.executeUpdate());
println("T4: Inserted record (" + firstKey + ",-1,-1)" );
con2.commit();
println("T4: commit");
} catch (SQLException e) {
con2.rollback();
throw e;
}
println("T1: read previous tuple");
rs.previous(); // Go back to first tuple
println("T1: id=" + rs.getInt(1));
rs.updateInt(2, 3);
println("T1: updateInt(2, 3);");
rs.updateRow();
println("T1: updated column 2, to value=3");
println("T1: commit");
commit();
rs = s.executeQuery("select * from t1");
while (rs.next()) {
println("T5: Read Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
}
con2.close();
}
/**
* Test what happens if you update a tuple which is deleted, purged and
* then reinserted with the exact same values
**/
public void testUpdatePurgedTuple4()
throws SQLException
{
getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
Statement s = createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1");
rs.next(); // Point to first tuple
int firstKey = rs.getInt(1);
int valA = rs.getInt(2);
int valB = rs.getInt(3);
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
rs.next(); // Go to next
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
Connection con2 = openDefaultConnection();
con2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
try {
PreparedStatement ps2 = con2.prepareStatement
("delete from t1 where id=?");
ps2.setInt(1, firstKey);
assertEquals("Expected one record to be deleted", 1,
ps2.executeUpdate());
println("T2: Deleted record with id=" + firstKey);
con2.commit();
println("T2: commit");
// Now purge the table
ps2 = con2.prepareStatement
("call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE(?,?,?,?,?)");
ps2.setString(1, "APP"); // schema
ps2.setString(2, "T1"); // table name
ps2.setInt(3, 1); // purge
ps2.setInt(4, 0); // defragment rows
ps2.setInt(5, 0); // truncate end
println("T3: call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE");
println("T3: purges deleted records");
ps2.executeUpdate();
con2.commit();
println("T3: commit");
ps2 = con2.prepareStatement("insert into t1 values(?,?,?,?,?)");
ps2.setInt(1, firstKey);
ps2.setInt(2, valA);
ps2.setInt(3, valB);
ps2.setString(4, "UPDATE TUPLE " + firstKey);
ps2.setString(5, "UPDATED CLOB " + firstKey);
assertEquals("Expected one record to be inserted", 1,
ps2.executeUpdate());
println("T4: Inserted record (" + firstKey + "," + valA + "," +
valB + ")" );
con2.commit();
println("T4: commit");
} catch (SQLException e) {
con2.rollback();
throw e;
}
rs.previous(); // Go back to first tuple
println("T1: Read previous Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
println("T1: id=" + rs.getInt(1));
rs.updateInt(2, 3);
rs.updateRow();
println("T1: updated column 2, to value=3");
println("T1: commit");
commit();
rs = s.executeQuery("select * from t1");
while (rs.next()) {
println("T4: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
}
con2.close();
}
/**
* Test what happens if you update a tuple which has been modified by
* another transaction.
**/
public void testUpdateModifiedTuple1()
throws SQLException
{
getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
Statement s = createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1");
rs.next(); // Point to first tuple
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
int firstKey = rs.getInt(1);
rs.next(); // Go to next
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
Connection con2 = openDefaultConnection();
con2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
try {
PreparedStatement ps2 = con2.prepareStatement
("update t1 set b=? where id=?");
ps2.setInt(1, 999);
ps2.setInt(2, firstKey);
assertEquals("Expected one record to be updated", 1,
ps2.executeUpdate());
println("T2: Updated b=999 where id=" + firstKey);
con2.commit();
println("T2: commit");
} catch (SQLException e) {
con2.rollback();
throw e;
}
rs.previous(); // Go back to first tuple
println("T1: Read previous Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
rs.updateInt(2, 3);
rs.updateRow();
println("T1: updated column 2, to value=3");
commit();
println("T1: commit");
rs = s.executeQuery("select * from t1");
while (rs.next()) {
println("T3: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
}
con2.close();
}
/**
* Test what happens if you update a tuple which has been modified by
* another transaction (in this case the same column)
**/
public void testUpdateModifiedTuple2()
throws SQLException
{
getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
Statement s = createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1");
rs.next(); // Point to first tuple
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
int firstKey = rs.getInt(1);
rs.next(); // Go to next
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
Connection con2 = openDefaultConnection();
con2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
try {
PreparedStatement ps2 = con2.prepareStatement
("update t1 set b=? where id=?");
ps2.setInt(1, 999);
ps2.setInt(2, firstKey);
assertEquals("Expected one record to be updated", 1,
ps2.executeUpdate());
println("T2: Updated b=999 where id=" + firstKey);
con2.commit();
println("T2: commit");
} catch (SQLException e) {
con2.rollback();
throw e;
}
rs.previous(); // Go back to first tuple
println("T1: Read previous Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
rs.updateInt(3, 9999);
rs.updateRow();
println("T1: updated column 3, to value=9999");
commit();
println("T1: commit");
rs = s.executeQuery("select * from t1");
while (rs.next()) {
println("T3: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
}
con2.close();
}
/**
* Tests that a ResultSet opened even in read uncommitted, gets a
* table intent lock, and that another transaction then cannot compress
* the table while the ResultSet is open.
**/
public void testTableIntentLock1()
throws SQLException
{
getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
Statement s = createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
println("T1: select * from t1");
ResultSet rs = s.executeQuery("select * from t1 for update");
while (rs.next()) {
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
} // Now the cursor does not point to any tuples
// Compressing the table in another transaction:
Connection con2 = openDefaultConnection();
con2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
PreparedStatement ps2 = con2.prepareStatement
("call SYSCS_UTIL.SYSCS_COMPRESS_TABLE(?, ?, ?)");
ps2.setString(1, "APP");
ps2.setString(2, "T1");
ps2.setInt(3, 0);
println("T2: call SYSCS_UTIL.SYSCS_COMPRESS_TABLE(APP, T1, 0)");
try {
ps2.executeUpdate(); // This will hang
fail("Expected T2 to hang");
} catch (SQLException e) {
println("T2: Got exception:" + e.getMessage());
assertSQLState(LOCK_TIMEOUT_EXPRESSION_SQL_STATE, e);
}
ps2.close();
con2.rollback();
con2.close();
s.close();
}
/**
* Test that Derby set updatelock on current row when using
* read-uncommitted
**/
public void testUpdateLockInReadUncommitted()
throws SQLException
{
getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
Statement s = createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1");
rs.next();
int firstKey = rs.getInt(1);
println("T1: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
Connection con2 = openDefaultConnection();
con2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
PreparedStatement ps2 = con2.prepareStatement
("delete from t1 where id=?");
ps2.setInt(1, firstKey);
try {
ps2.executeUpdate();
fail("expected record with id=" + firstKey +
" to be locked");
} catch (SQLException e) {
assertSQLState(LOCK_TIMEOUT_SQL_STATE, e);
}
ps2.close();
con2.rollback();
con2.close();
s.close();
}
/**
* Test that the system cannot defragment any records
* as long as an updatable result set is open against the table.
**/
public void testDefragmentDuringScan()
throws SQLException
{
testCompressDuringScan(true, false);
}
/**
* Test that the system cannot truncate any records
* as long as an updatable result set is open against the table.
**/
public void testTruncateDuringScan()
throws SQLException
{
testCompressDuringScan(false, true);
}
/**
* Test that the system does not purge any records
* as long as we do either a defragment, or truncate
**/
private void testCompressDuringScan(boolean testDefragment,
boolean testTruncate)
throws SQLException
{
getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
Statement delStatement = createStatement();
// First delete all records except the last and first
int deleted = delStatement.executeUpdate
("delete from T1 where id>0 and id<" + (recordCount-1));
int expectedDeleted = recordCount-2;
println("T1: delete records");
assertEquals("Invalid number of records deleted", expectedDeleted,
deleted);
delStatement.close();
commit();
println("T1: commit");
Statement s = createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = s.executeQuery("select * from t1");
rs.next();
int firstKey = rs.getInt(1);
println("T2: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
int lastKey = firstKey;
while (rs.next()) {
lastKey = rs.getInt(1);
println("T2: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
}
final Connection con2 = openDefaultConnection();
con2.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
final PreparedStatement ps2 = con2.prepareStatement
("call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE(?,?,?,?,?)");
ps2.setString(1, "APP"); // schema
ps2.setString(2, "T1"); // table name
ps2.setInt(3, 0); // purge
int defragment = testDefragment ? 1 : 0;
int truncate = testTruncate ? 1 : 0;
ps2.setInt(4, defragment); // defragment rows
ps2.setInt(5, truncate); // truncate end
println("T3: call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE");
println("T3: defragmenting rows");
try {
ps2.executeUpdate();
con2.commit();
println("T3: commit");
fail("Expected T3 to hang waiting for Table lock");
} catch (SQLException e) {
println("T3: got expected exception");
con2.rollback();
}
ps2.close();
rs.first(); // Go to first tuple
println("T1: Read first Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
rs.updateInt(2, 3);
println("T1: updateInt(2, 3);");
rs.updateRow();
println("T1: updateRow()");
rs.last(); // Go to last tuple
println("T1: Read last Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
rs.updateInt(2, 3);
println("T1: updateInt(2, 3);");
rs.updateRow();
println("T1: updateRow()");
commit();
println("T1: commit");
rs = s.executeQuery("select * from t1");
println("T4: select * from table");
while (rs.next()) {
println("T4: Read next Tuple:(" + rs.getInt(1) + "," +
rs.getInt(2) + "," +
rs.getInt(3) + ")");
}
con2.close();
s.close();
}
// By providing a static suite(), you can customize which tests to run.
// The default is to run all tests in the TestCase subclass.
/**
* Run in embedded and client.
*/
public static Test suite()
{
final BaseTestSuite suite = new BaseTestSuite("ConcurrencyTest");
suite.addTest(baseSuite("ConcurrencyTest:embedded", true));
suite.addTest(
TestConfiguration.clientServerDecorator(
baseSuite("ConcurrencyTest:client", false)));
// Since this test relies on lock waiting, setting this property will
// make it go a lot faster:
return DatabasePropertyTestSetup.setLockTimeouts(suite, -1, 4);
}
private static Test baseSuite(String name, boolean embedded) {
final BaseTestSuite suite = new BaseTestSuite(name);
// This testcase does not require JDBC3/JSR169, since it does not
// specify result set concurrency) in Connection.createStatement().
suite.addTest(new ConcurrencyTest("testSharedLocks2"));
// The following testcases requires JDBC3/JSR169:
if ((JDBC.vmSupportsJDBC3() || JDBC.vmSupportsJSR169())) {
// The following testcases do not use updatable result sets:
suite.addTest(new ConcurrencyTest("testUpdateLockDownGrade1"));
suite.addTest(new ConcurrencyTest("testAquireUpdateLock1"));
suite.addTest(new ConcurrencyTest("testSharedLocks1"));
suite.addTest(new ConcurrencyTest("testSharedAndUpdateLocks1"));
suite.addTest(new ConcurrencyTest("testSharedAndUpdateLocks2"));
suite.addTest(new ConcurrencyTest ("testUpdatePurgedTuple2"));
suite.addTest(new ConcurrencyTest("testUpdatePurgedTuple3"));
suite.addTest(new ConcurrencyTest("testUpdatePurgedTuple4"));
suite.addTest(new ConcurrencyTest("testUpdateModifiedTuple1"));
suite.addTest(new ConcurrencyTest("testUpdateModifiedTuple2"));
suite.addTest(new ConcurrencyTest("testTableIntentLock1"));
suite.addTest
(new ConcurrencyTest("testUpdateLockInReadUncommitted"));
suite.addTest(new ConcurrencyTest("testDefragmentDuringScan"));
suite.addTest(new ConcurrencyTest("testTruncateDuringScan"));
// This testcase fails in DerbyNetClient framework due to
// DERBY-1696
if (embedded) {
suite.addTest
(new ConcurrencyTest("testUpdatePurgedTuple1"));
}
}
return suite;
}
}