blob: 6a843fec7b4132b83110cfe1506a9c25d820236f [file] [log] [blame]
/*
*
* Derby - Class org.apache.derbyTesting.functionTests.tests.jdbcapi/SavepointJdbc30Test
*
* 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.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.TestConfiguration;
/**
* Test the new class Savepoint in JDBC 30. Also, test some mix and match of
* defining savepoints through JDBC and SQL testing both callable and prepared
* statements meta data
*
*/
public class SavepointJdbc30Test extends BaseJDBCTestCase {
/**
* Create a test
*
* @param name
*/
public SavepointJdbc30Test(String name) {
super(name);
}
/**
* Set up the test suite for embedded mode, client mode, and embedded mode
* with XADataSources
*
* @return A suite containing embedded, client and embedded with XA suites
*/
public static Test suite() {
BaseTestSuite suite =
new BaseTestSuite("SavepointJdbc30_JSR169Test suite");
// Get the tests for embedded
BaseTestSuite embedded = new BaseTestSuite(
"SavepointJdbc30_JSR169Test:embedded");
embedded.addTestSuite(SavepointJdbc30Test.class);
embedded.addTest(getEmbeddedSuite("SavepointJdbc30_JSR169Test:"
+ "embedded only"));
suite.addTest(embedded);
// Get the tests for client.
BaseTestSuite client =
new BaseTestSuite("SavepointJdbc30_JSR169Test:client");
client.addTestSuite(SavepointJdbc30Test.class);
suite.addTest(TestConfiguration.clientServerDecorator(client));
// Repeat the embedded tests obtaining a connection from
// an XA data source if it is supported. This is not supported
// under JSR169.
if (JDBC.vmSupportsJDBC3()) {
embedded = new BaseTestSuite(
"SavepointJdbc30_JSR169Test:embedded XADataSource");
embedded.addTestSuite(SavepointJdbc30Test.class);
embedded.addTest(getEmbeddedSuite("SavepointJdbc30_JSR169Test:"
+ "embedded only XADataSource"));
suite.addTest(TestConfiguration.connectionXADecorator(embedded));
// Repeat the client tests obtaining a connection from
// an XA data source if it is supported. This is not supported
// under JSR169.
client = new BaseTestSuite(
"SavepointJdbc30_JSR169Test:client XADatasource");
client.addTestSuite(SavepointJdbc30Test.class);
suite.addTest(TestConfiguration.clientServerDecorator(TestConfiguration.connectionXADecorator(client)));
}
return new CleanDatabaseTestSetup(
DatabasePropertyTestSetup.setLockTimeouts(suite, 1, 2)) {
/**
* Creates the database objects used in the test cases.
*
* @throws SQLException
*/
protected void decorateSQL(Statement s) throws SQLException {
/* Create a table */
s.execute("create table t1 (c11 int, c12 smallint)");
s.execute("create table t2 (c11 int)");
getConnection().commit();
}
};
}
/**
* Create a testsuite containing the tests that can only run in embedded
* mode. These tests have names starting with x and are added automatically.
*/
private static Test getEmbeddedSuite(String name) {
BaseTestSuite embedded = new BaseTestSuite(name);
Method[] methods = SavepointJdbc30Test.class.getMethods();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getParameterTypes().length > 0
|| !m.getReturnType().equals(Void.TYPE)) {
continue;
}
String methodName = m.getName();
if (methodName.startsWith("x")) {
embedded.addTest(new SavepointJdbc30Test(methodName));
}
}
return embedded;
}
/**
* Set up the test environment.
*/
protected void setUp() throws Exception {
super.setUp();
// Keep Autocommit off
getConnection().setAutoCommit(false);
// Clear the tables created by the decorator
Statement s = createStatement();
s.execute("truncate table t1");
s.execute("truncate table t2");
commit();
}
/**
* Test1. It should not be possible to set a savepoint if autocommit is on.
*/
public void testNoSavepointsIfAutoCommit() throws SQLException {
Connection con = getConnection();
con.setAutoCommit(true);
try {
con.setSavepoint(); // will throw exception because auto commit is
// true
fail("No unnamed savepoints allowed if autocommit is true");
} catch (SQLException se) {
// Expected exception.
assertSQLState("XJ010", se);
}
// Test 1a
try {
con.setSavepoint("notallowed"); // will throw exception because auto
// commit is true
fail("No named savepoints allowed if autocommit is true");
} catch (SQLException se) {
// Expected exception.
assertSQLState("XJ010", se);
}
}
/**
* Test2 - After releasing a savepoint, should be able to reuse it.
*/
public void testReusingSavepoints() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("s1");
con.releaseSavepoint(savepoint1);
con.setSavepoint("s1");
con.rollback();
}
/**
* Test3 - Named savepoints can't pass null for name
*/
public void testNullName() throws SQLException {
Connection con = getConnection();
try {
con.setSavepoint(null);
fail("FAIL 3 Null savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("XJ011", se);
}
con.rollback();
}
/**
* Test4 - Verify names/ids of named/unnamed savepoints named savepoints
* don't have an id. unnamed savepoints don't have a name (internally, all
* our savepoints have names, but for unnamed savepoint, that is not exposed
* through jdbc api)
*
* @throws SQLException
*/
public void testNamesAndIds() throws SQLException {
Connection con = getConnection();
try {
Savepoint savepoint1 = con.setSavepoint();
savepoint1.getSavepointId();
// following should throw exception for unnamed savepoint
savepoint1.getSavepointName();
fail("FAIL 4 getSavepointName on id savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("XJ014", se);
}
con.rollback();
try {
Savepoint savepoint1 = con.setSavepoint("s1");
savepoint1.getSavepointName();
// following should throw exception for named savepoint
savepoint1.getSavepointId();
fail("FAIL 4 getSavepointId on named savepoint ");
} catch (SQLException se) {
// Expected exception.
assertSQLState("XJ013", se);
}
con.rollback();
}
/**
* TEST 5a and 5b for bug 4465 test 5a - create two savepoints in two
* different transactions and release the first one in the subsequent
* transaction
*/
public void testBug4465() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("s1");
con.commit();
// The following savepoint was earlier named s1. Changed it to s2 while
// working on DRDA support
// for savepoints. The reason for that is as follows
// The client translates all savepoint jdbc calls to equivalent sql and
// hence
// if the 2 savepoints in
// different connections are named the same, then the release savepoint
// below will get converted to
// RELEASE TO SAVEPOINT s1 and that succeeds because the 2nd connection
// does have a savepoint named s1.
// Hence we don't really check what we intended to check which is trying
// to release a savepoint created
// in a different transaction
con.setSavepoint("s2");
Statement s = createStatement();
s.executeUpdate("INSERT INTO T1 VALUES(2,1)");
try {
con.releaseSavepoint(savepoint1);
fail("FAIL 5a - release savepoint from a different transaction "
+ "did not raise error");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
con.commit();
// test 5b - create two savepoints in two different transactions
// and rollback the first one in the subsequent transaction
savepoint1 = con.setSavepoint("s1");
con.commit();
// The following savepoint was earlier named s1. Changed it to s2 while
// working on DRDA support
// for savepoints. The reason for that is as follows
// The client translates all savepoint jdbc calls to equivalent sql and
// hence
// if the 2 savepoints in
// different connections are named the same, then the rollback savepoint
// below will get converted to
// ROLLBACK TO SAVEPOINT s1 and that succeeds because the 2nd connection
// does have a savepoint named s1.
// Hence we don't really check what we intended to check which is trying
// to rollback a savepoint created
// in a different transaction
con.setSavepoint("s2");
s.executeUpdate("INSERT INTO T1 VALUES(2,1)");
try {
con.rollback(savepoint1);
fail("FAIL 5b - rollback savepoint from a different transaction "
+ "did not raise error");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
con.commit();
}
/**
* test 6a - create a savepoint release it and then create another with the
* same name. and release the first one
*/
public void testReleaseReleasedSavepoint() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("s1");
con.releaseSavepoint(savepoint1);
// The following savepoint was earlier named s1. Changed it to s2 while
// working on DRDA support
// for savepoints. The reason for that is as follows
// The client translates all savepoint jdbc calls to equivalent sql and
// hence
// if the 2 savepoints in
// a transaction are named the same, then the release savepoint below
// will get converted to
// RELEASE TO SAVEPOINT s1 and that succeeds because there is a valid
// savepoint named s1.
con.setSavepoint("s2");
Statement s = createStatement();
s.executeUpdate("INSERT INTO T1 VALUES(2,1)");
try {
con.releaseSavepoint(savepoint1);
fail("FAIL 6a - releasing a released savepoint did not raise error");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
con.commit();
}
/**
* test 6b - create a savepoints release it and then create another with the
* same name. and rollback the first one
*/
public void testRollbackReleasedSavepoint() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("s1");
con.releaseSavepoint(savepoint1);
// The following savepoint was earlier named s1. Changed it to s2 while
// working on DRDA support
// for savepoints. The reason for that is as follows
// The client translates all savepoint jdbc calls to equivalent sql and
// hence
// if the 2 savepoints in
// a transaction are named the same, then the rollback savepoint below
// will get converted to
// ROLLBACK TO SAVEPOINT s1 and that succeeds because there is a valid
// savepoint named s1.
con.setSavepoint("s2");
Statement s = createStatement();
s.executeUpdate("INSERT INTO T1 VALUES(2,1)");
try {
con.rollback(savepoint1);
fail("FAIL 6b - rollback a released savepoint did not raise error");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
con.commit();
}
/**
* Test 6c: TEST case just for bug 4467 // Test 10 - create a named
* savepoint with the a generated name savepoint1 =
* con2.setSavepoint("SAVEPT0"); // what exactly is the correct behaviour
* here? try { savepoint2 = con2.setSavepoint(); } catch (SQLException se) {
* System.out.println("Expected Exception is " + se.getMessage()); }
* con2.commit();
*/
public void testReleaseSavepointFromOtherTransaction() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("s1");
Statement s = createStatement();
s.executeUpdate("INSERT INTO T1 VALUES(2,1)");
Connection con2 = openDefaultConnection();
try {
con2.releaseSavepoint(savepoint1);
fail("FAIL 6c - releasing another transaction's savepoint did "
+ "not raise error");
} catch (SQLException se) {
// Expected exception.
if (usingEmbedded()) {
assertSQLState("XJ010", se);
} else if (usingDerbyNetClient()) {
assertSQLState("XJ008", se);
}
}
con.commit();
con2.commit();
}
/**
* Test 7a: BUG 4468 - should not be able to pass a savepoint from a
* different transaction for release/rollback
*/
public void testSwapSavepointsAcrossConnectionAndRelease()
throws SQLException {
Connection con = getConnection();
Connection con2 = openDefaultConnection();
con2.setAutoCommit(false);
Savepoint savepoint1 = con2.setSavepoint("s1");
Statement s = createStatement();
s.executeUpdate("INSERT INTO T1 VALUES(2,1)");
con.setSavepoint("s1");
try {
con.releaseSavepoint(savepoint1);
fail("FAIL 7a - releasing a another transaction's savepoint did "
+ "not raise error");
} catch (SQLException se) {
// Expected exception.
if (usingEmbedded()) {
assertSQLState("3B502", se);
} else if (usingDerbyNetClient()) {
assertSQLState("XJ097", se);
}
}
con.commit();
con2.commit();
}
/**
* Test 7b - swap savepoints across connections
*/
public void testSwapSavepointsAcrossConnectionsAndRollback()
throws SQLException {
Connection con = getConnection();
Connection con2 = openDefaultConnection();
con2.setAutoCommit(false);
Savepoint savepoint1 = con2.setSavepoint("s1");
Statement s = createStatement();
s.executeUpdate("INSERT INTO T1 VALUES(2,1)");
con.setSavepoint("s1");
try {
con.rollback(savepoint1);
fail("FAIL 7b - rolling back a another transaction's savepoint "
+ "did not raise error");
} catch (SQLException se) {
// Expected exception.
if (usingEmbedded()) {
assertSQLState("3B502", se);
} else if (usingDerbyNetClient()) {
assertSQLState("XJ097", se);
}
}
con.commit();
con2.commit();
}
/*
* following section attempts to call statement in a method to do a negative
* test because savepoints are not supported in a trigger however, this
* cannot be done because a call is not supported in a trigger. leaving the
* test here for later reference for when we support the SQL version // bug
* 4507 - Test 8 test all 4 savepoint commands inside the trigger code
* System.out.println("Test 8a set savepoint(unnamed) command inside the
* trigger code"); s.executeUpdate("create trigger trig1 before insert on t1
* for each statement call
* org.apache.derbyTesting.functionTests.tests.jdbcapi.savepointJdbc30::doConnectionSetSavepointUnnamed()");
* try {
*
* s.executeUpdate("insert into t1 values(1,1)"); System.out.println("FAIL
* 8a set savepoint(unnamed) command inside the trigger code"); } catch
* (SQLException se) { System.out.println("Expected Exception is " +
* se.getMessage()); } s.executeUpdate("drop trigger trig1");
*
* System.out.println("Test 8b set savepoint(named) command inside the
* trigger code"); s.executeUpdate("create trigger trig2 before insert on t1
* for each statement call
* org.apache.derbyTesting.functionTests.tests.jdbcapi.savepointJdbc30::doConnectionSetSavepointNamed()");
* try { s.executeUpdate("insert into t1 values(1,1)");
* System.out.println("FAIL 8b set savepoint(named) command inside the
* trigger code"); } catch (SQLException se) { System.out.println("Expected
* Exception is " + se.getMessage()); } s.executeUpdate("drop trigger
* trig2");
*
* System.out.println("Test 8c release savepoint command inside the trigger
* code"); s.executeUpdate("create trigger trig3 before insert on t1 for
* each statement call
* org.apache.derbyTesting.functionTests.tests.jdbcapi.savepointJdbc30::doConnectionReleaseSavepoint()");
* try { s.executeUpdate("insert into t1 values(1,1)");
* System.out.println("FAIL 8c release savepoint command inside the trigger
* code"); } catch (SQLException se) { System.out.println("Expected
* Exception is " + se.getMessage()); } s.executeUpdate("drop trigger
* trig3");
*
* System.out.println("Test 8d rollback savepoint command inside the trigger
* code"); s.executeUpdate("create trigger trig4 before insert on t1 for
* each statement call
* org.apache.derbyTesting.functionTests.tests.jdbcapi.savepointJdbc30::doConnectionRollbackSavepoint()");
* try { s.executeUpdate("insert into t1 values(1,1)");
* System.out.println("FAIL 8d rollback savepoint command inside the trigger
* code"); } catch (SQLException se) { System.out.println("Expected
* Exception is " + se.getMessage()); } s.executeUpdate("drop trigger
* trig4"); con.rollback();
*/// end commented out test 8
/**
* Test 9 test savepoint name and verify case sensitivity
*/
public void testSavepointName() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("myname");
String savepointName = savepoint1.getSavepointName();
assertEquals(savepointName, "myname");
con.rollback();
}
/**
* Test 10 test savepoint name case sensitivity
*/
public void testNameCaseSensitivity() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("MyName");
String savepointName = savepoint1.getSavepointName();
assertEquals(savepointName, "MyName");
con.rollback();
}
/**
* Test 11 rolling back a savepoint multiple times - should work
*/
public void testRollbackMultipleTimes() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("MyName");
con.rollback(savepoint1);
con.rollback(savepoint1);
con.rollback();
}
/**
* Test 12 releasing a savepoint multiple times - should not work
*/
public void testReleaseMultipleTimes() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("MyName");
con.releaseSavepoint(savepoint1);
try {
con.releaseSavepoint(savepoint1);
fail("FAIL 12 releasing a savepoint multiple times should fail");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
con.rollback();
}
/**
* Test 13 shouldn't be able to use a savepoint from earlier transaction
* after setting autocommit on and off
*/
public void testSavepointFromEarlierTransactionAfterToggleAutocommit()
throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("MyName");
con.setAutoCommit(true);
con.setAutoCommit(false);
Savepoint savepoint2 = con.setSavepoint("MyName1");
try {// shouldn't be able to use savepoint from earlier tranasaction
// after setting autocommit on and off
con.releaseSavepoint(savepoint1);
fail("FAIL 13 shouldn't be able to use a savepoint from earlier "
+ "transaction after setting autocommit on and off");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
con.releaseSavepoint(savepoint2);
con.rollback();
}
/**
* Test 14 cause a transaction rollback and that should release the internal
* savepoint array
*/
public void testRollbackReleasesSavepointArray() throws SQLException {
Connection con = getConnection();
Connection con2 = openDefaultConnection();
con2.setAutoCommit(false);
Statement s1, s2;
s1 = createStatement();
s1.executeUpdate("insert into t1 values(1,1)");
s1.executeUpdate("insert into t1 values(2,0)");
con.commit();
s1.executeUpdate("update t1 set c11=c11+1 where c12 > 0");
s2 = con2.createStatement();
Savepoint savepoint1 = con2.setSavepoint("MyName");
try {// following will get lock timeout which will rollback
// transaction on c2
s2.executeUpdate("update t1 set c11=c11+1 where c12 < 1");
fail("FAIL 14 should have gotten lock time out");
} catch (SQLException se) {
// Expected exception.
assertSQLState("40XL1", se);
}
try {// the transaction rollback above should have removed the
// savepoint MyName
con2.releaseSavepoint(savepoint1);
fail("FAIL 14 A non-user initiated transaction rollback should "
+ "release the internal savepoint array");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
con.rollback();
con2.rollback();
s1.execute("delete from t1");
con.commit();
}
/** Test 15 Check savepoints in batch */
public void testSavepointsInBatch() throws SQLException {
Connection con = getConnection();
Statement s = createStatement();
s.execute("delete from t1");
s.addBatch("insert into t1 values(1,1)");
s.addBatch("insert into t1 values(2,2)");
Savepoint savepoint1 = con.setSavepoint();
s.addBatch("insert into t1 values(3,3)");
s.executeBatch();
con.rollback(savepoint1);
assertTableRowCount("T1", 0);
con.rollback();
}
/** Test 16 grammar check for savepoint sq1 */
public void testGrammarCheck() throws SQLException {
Statement s = getConnection().createStatement();
try {
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS");
fail("FAIL 16 Should have gotten exception for missing ON ROLLBACK "
+ "RETAIN CURSORS");
} catch (SQLException se) {
// Expected exception.
assertSQLState("42X01", se);
}
try {
s.executeUpdate("SAVEPOINT s1 UNIQUE ON ROLLBACK RETAIN CURSORS "
+ "ON ROLLBACK RETAIN CURSORS");
fail("FAIL 16 Should have gotten exception for multiple ON ROLLBACK "
+ "RETAIN CURSORS");
} catch (SQLException se) {
// Expected exception.
assertSQLState("42613", se);
}
try {
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON ROLLBACK"
+ " RETAIN LOCKS");
fail("FAIL 16 Should have gotten exception for multiple ON ROLLBACK "
+ "RETAIN LOCKS");
} catch (SQLException se) {
// Expected exception.
assertSQLState("42613", se);
}
try {
s.executeUpdate("SAVEPOINT s1 UNIQUE UNIQUE ON ROLLBACK RETAIN "
+ "LOCKS ON ROLLBACK RETAIN CURSORS");
fail("FAIL 16 Should have gotten exception for multiple UNIQUE keywords");
} catch (SQLException se) {
// Expected exception.
assertSQLState("42613", se);
}
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN CURSORS ON ROLLBACK "
+ "RETAIN LOCKS");
s.executeUpdate("RELEASE TO SAVEPOINT s1");
getConnection().rollback();
}
/** Test 17 */
public void testNoNestedSavepointsWhenUsingSQL() throws SQLException {
Statement s = getConnection().createStatement();
s.executeUpdate("SAVEPOINT s1 UNIQUE ON ROLLBACK RETAIN LOCKS ON "
+ "ROLLBACK RETAIN CURSORS");
try {
s.executeUpdate("SAVEPOINT s2 UNIQUE ON ROLLBACK RETAIN "
+ "LOCKS ON ROLLBACK RETAIN CURSORS");
fail("FAIL 17a Should have gotten exception for nested savepoints");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B002", se);
}
s.executeUpdate("RELEASE TO SAVEPOINT s1");
s.executeUpdate("SAVEPOINT s2 UNIQUE ON ROLLBACK RETAIN LOCKS ON "
+ "ROLLBACK RETAIN CURSORS");
getConnection().rollback();
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON ROLLBACK "
+ "RETAIN CURSORS");
try {
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON "
+ "ROLLBACK RETAIN CURSORS");
fail("FAIL 17b Should have gotten exception for nested savepoints");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B002", se);
}
getConnection().rollback();
}
/** Test 18 */
public void testNoNestedSavepointsInsideJdbcSavepoint() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint();
Statement s = getConnection().createStatement();
// Following SQL savepoint will fail because we are trying to nest it
// inside JDBC savepoint
try {
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON ROLLBACK"
+ " RETAIN CURSORS");
fail("FAIL 18 shouldn't be able set SQL savepoint nested inside "
+ "JDBC savepoints");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B002", se);
}
// rollback the JDBC savepoint. Now since there are no user defined
// savepoints, we can define SQL savepoint
con.releaseSavepoint(savepoint1);
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON "
+ "ROLLBACK RETAIN CURSORS");
con.rollback();
}
/** Test 19 */
public void testNoNestedSavepointsInsideSqlSavepoint() throws SQLException {
Statement s = getConnection().createStatement();
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON ROLLBACK "
+ "RETAIN CURSORS");
try {
s.executeUpdate("SAVEPOINT s2 ON ROLLBACK RETAIN LOCKS ON ROLLBACK"
+ " RETAIN CURSORS");
fail("FAIL 19 shouldn't be able set SQL savepoint nested inside "
+ "SQL savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B002", se);
}
// rollback the SQL savepoint. Now since there are no user defined
// savepoints, we can define SQL savepoint
s.executeUpdate("RELEASE TO SAVEPOINT s1");
s.executeUpdate("SAVEPOINT s2 ON ROLLBACK RETAIN LOCKS ON ROLLBACK "
+ "RETAIN CURSORS");
getConnection().rollback();
}
/** Test 20 */
public void testRollbackSqlSavepointSameAsJdbc() throws SQLException {
Connection con = getConnection();
Statement s = createStatement();
s.executeUpdate("DELETE FROM T1");
con.commit();
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON ROLLBACK "
+ "RETAIN CURSORS");
s.executeUpdate("INSERT INTO T1 VALUES(1,1)");
s.executeUpdate("INSERT INTO T1 VALUES(1,1)");
s.executeUpdate("INSERT INTO T1 VALUES(1,1)");
// Rollback to SQL savepoint and should see changes rolledback
s.execute("ROLLBACK TO SAVEPOINT s1");
ResultSet rs1 = s.executeQuery("select count(*) from t1");
rs1.next();
assertEquals(rs1.getInt(1), 0);
con.rollback();
}
/** Test 21 */
public void testReleaseSqlSavepointAndRollback() throws SQLException {
Connection con = getConnection();
Statement s = createStatement();
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON ROLLBACK "
+ "RETAIN CURSORS");
s.executeUpdate("INSERT INTO T1 VALUES(1,1)");
s.executeUpdate("INSERT INTO T1 VALUES(2,1)");
s.executeUpdate("INSERT INTO T1 VALUES(3,1)");
// Release the SQL savepoint and then rollback the transaction and
// should see changes rolledback
s.executeUpdate("RELEASE TO SAVEPOINT s1");
con.rollback();
ResultSet rs1 = s.executeQuery("select count(*) from t1");
rs1.next();
assertEquals(rs1.getInt(1), 0);
con.rollback();
}
/** Test 22 */
public void testNoSqlSavepointStartingWithSYS() throws SQLException {
Statement s = createStatement();
try {
s.executeUpdate("SAVEPOINT SYSs2 ON ROLLBACK RETAIN LOCKS ON "
+ "ROLLBACK RETAIN CURSORS");
fail("FAIL 22 shouldn't be able to create a SQL savepoint starting "
+ "with name SYS");
} catch (SQLException se) {
// Expected exception.
assertSQLState("42939", se);
}
getConnection().rollback();
}
/**
* Test 23 - bug 5817 - make savepoint and release non-reserved keywords
*/
public void testBug5817() throws SQLException {
Statement s = createStatement();
s.execute("create table savepoint (savepoint int, release int)");
ResultSet rs1 = s.executeQuery("select count(*) from savepoint");
rs1.next();
assertEquals(" There should have been 0 rows in the table, but found "
+ rs1.getInt(1), rs1.getInt(1), 0);
s.execute("SAVEPOINT savepoint ON ROLLBACK RETAIN LOCKS ON ROLLBACK "
+ "RETAIN CURSORS");
s.executeUpdate("INSERT INTO savepoint VALUES(1,1)");
s.execute("RELEASE SAVEPOINT savepoint");
rs1 = s.executeQuery("select count(*) from savepoint");
rs1.next();
assertEquals("There should have been 1 rows in the table, but found "
+ rs1.getInt(1), rs1.getInt(1), 1);
s.execute("SAVEPOINT release ON ROLLBACK RETAIN LOCKS ON ROLLBACK "
+ "RETAIN CURSORS");
s.executeUpdate("INSERT INTO savepoint VALUES(2,1)");
s.execute("ROLLBACK TO SAVEPOINT release");
rs1 = s.executeQuery("select count(*) from savepoint");
rs1.next();
assertEquals(
"ERROR: There should have been 1 rows in the table, but found "
+ rs1.getInt(1), rs1.getInt(1), 1);
s.execute("RELEASE SAVEPOINT release");
getConnection().rollback();
}
/**
* Test 24 Savepoint name can't exceed 128 characters
*/
public void testNameLengthMax128Chars() throws SQLException {
try {
getConnection()
.setSavepoint(
"MyName12345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789012345"
+ "678901234567890123456789012345678901234567890");
fail("FAIL 24 shouldn't be able to create a SQL savepoint with "
+ "name exceeding 128 characters");
} catch (SQLException se) {
// Expected exception.
assertSQLState("42622", se);
}
getConnection().rollback();
}
/** Test 25 */
public void testNoSqlSavepointStartingWithSYSThroughJdbc()
throws SQLException {
try {
getConnection().setSavepoint("SYSs2");
fail("FAIL 25 shouldn't be able to create a SQL savepoint starting with name SYS through jdbc");
} catch (SQLException se) {
// Expected exception.
assertSQLState("42939", se);
}
getConnection().rollback();
}
/**
* bug 4451 - Test 26a pass Null value to rollback bug 5374 - Passing a null
* savepoint to rollback or release method used to give a npe in JCC it
* should give a SQLException aying "Cannot rollback to a null savepoint"
*/
public void testRollbackNullSavepoint() throws SQLException {
try {
getConnection().rollback((Savepoint) null);
fail("FAIL 26a rollback of null savepoint did not raise error ");
} catch (SQLException se) {
// Expected exception.
if (usingEmbedded()) {
assertSQLState("3B001", se);
} else if (usingDerbyNetClient()) {
assertSQLState("3B502", se);
}
}
}
/**
* Test 26b pass Null value to releaseSavepoint
*/
public void testReleaseNullSavepoint() throws SQLException {
try {
getConnection().releaseSavepoint((Savepoint) null);
fail("FAIL 26b release of null savepoint did not raise error ");
} catch (SQLException se) {
// Expected exception.
if (usingEmbedded()) {
assertSQLState("3B001", se);
} else if (usingDerbyNetClient()) {
assertSQLState("3B502", se);
}
}
}
/**
* Test that savepoint names can have double-quote characters. The client
* driver used to fail with a syntax error when the names contained such
* characters. DERBY-5170.
*/
public void testQuotes() throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
s.execute("create table test_quotes(x int)");
s.execute("insert into test_quotes values 1");
Savepoint sp = getConnection().setSavepoint("a \" b ' c");
s.execute("insert into test_quotes values 2");
getConnection().rollback(sp);
JDBC.assertSingleValueResultSet(
s.executeQuery("select * from test_quotes"),
"1");
getConnection().releaseSavepoint(sp);
}
/** ********************* */
/*
* The following tests have nested savepoints through JDBC calls. When
* coming through the network client these nested JDBC savepoint calls are
* translated into equivalent SQL savepoint statements. But we do not allow
* nested savepoints coming through SQL statements and hence these tests
* can't be run under DRDA framework.
*/
/**
* Test40 - We internally generate a unique name for unnamed savepoints. If
* a named savepoint uses the currently used internal savepoint name, we
* won't get an exception thrown for it because we prepend external saves
* with "e." to avoid name conflicts.
*/
public void xtestNoConflictWithGeneratedName() throws SQLException {
Connection con = getConnection();
con.setSavepoint();
con.setSavepoint("i.SAVEPT0");
con.rollback();
}
/**
* Test41 - Rolling back to a savepoint will release all the savepoints
* created after that savepoint.
*/
public void xtestRollbackWillReleaseLaterSavepoints() throws SQLException {
Connection con = getConnection();
Statement s = createStatement();
// Make sure T1 is empty (testcase running order might have left content!):
s.execute("DELETE FROM T1");
Savepoint savepoint1 = con.setSavepoint();
s.executeUpdate("INSERT INTO T1 VALUES(1,1)");
Savepoint savepoint2 = con.setSavepoint("s1");
s.executeUpdate("INSERT INTO T1 VALUES(2,1)");
Savepoint savepoint3 = con.setSavepoint("s2");
s.executeUpdate("INSERT INTO T1 VALUES(3,1)");
// Rollback to first named savepoint s1. This will internally release
// the second named savepoint s2.
con.rollback(savepoint2);
assertTableRowCount("T1", 1);
// Trying to release second named savepoint s2 should throw exception.
try {
con.releaseSavepoint(savepoint3);
fail("FAIL 41a release of rolled back savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
// Trying to rollback second named savepoint s2 should throw exception.
try {
con.rollback(savepoint3);
fail("FAIL 41b release of rolled back savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
// Release the unnamed named savepoint.
con.rollback(savepoint1);
assertTableRowCount("T1", 0);
con.rollback();
}
/**
* Test42 - Rollback on a connection will release all the savepoints created
* for that transaction
*/
public void xtestRollbackWillReleaseActiveSavepoints() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint();
con.rollback();
try {
con.rollback(savepoint1);
fail("FAIL 42 release of rolled back savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
}
/**
* Test42a - Commit on a connection will release all the savepoints created
* for that transaction
*/
public void xtestCommitWillReleaseActiveSavepoints() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint();
con.commit();
try {
con.rollback(savepoint1);
fail("FAIL 42a Rollback after commit.");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B001", se);
}
}
/**
* Test43 - After releasing a savepoint, should be able to reuse it.
*/
public void xtestReuseNameAfterRelease() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("s1");
try {
con.setSavepoint("s1");
fail("Should not be able to set two savepoints with the same name");
} catch (SQLException se) {
// Expected exception.
if (usingEmbedded()) {
assertSQLState("3B501", se);
} else if (usingDerbyNetClient()) {
assertSQLState("3B002", se);
}
}
con.releaseSavepoint(savepoint1);
con.setSavepoint("s1");
con.rollback();
}
/**
* Test 45 reuse savepoint name after rollback - should not work
*/
public void xtestReuseNameAfterRollback() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint("MyName");
con.rollback(savepoint1);
try {
con.setSavepoint("MyName");
fail("FAIL 45 reuse of savepoint name after rollback should fail");
} catch (SQLException se) {
// Expected exception.
if (usingEmbedded()) {
assertSQLState("3B501", se);
} else if (usingDerbyNetClient()) {
assertSQLState("3B002", se);
}
}
con.rollback();
}
/**
* Test 46 bug 5145 Cursors declared before and within the savepoint unit
* will be closed when rolling back the savepoint
*/
public void xtestCursorsCloseOnRollback() throws SQLException {
Connection con = getConnection();
Statement sWithHold = con.createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
Statement s = createStatement();
s.executeUpdate("DELETE FROM T1");
s.executeUpdate("INSERT INTO T1 VALUES(19,1)");
s.executeUpdate("INSERT INTO T1 VALUES(19,2)");
s.executeUpdate("INSERT INTO T1 VALUES(19,3)");
ResultSet rs1 = s.executeQuery("select * from t1");
rs1.next();
ResultSet rs1WithHold = sWithHold.executeQuery("select * from t1");
rs1WithHold.next();
Savepoint savepoint1 = con.setSavepoint();
ResultSet rs2 = s.executeQuery("select * from t1");
rs2.next();
ResultSet rs2WithHold = sWithHold.executeQuery("select * from t1");
rs2WithHold.next();
con.rollback(savepoint1);
try {// resultset declared outside the savepoint unit should be
// closed at this point after the rollback to savepoint
rs1.next();
fail("FAIL 46 shouldn't be able to use a resultset (declared "
+ "before the savepoint unit) after the rollback to savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("XCL16", se);
}
try {// holdable resultset declared outside the savepoint unit should
// be closed at this point after the rollback to savepoint
rs1WithHold.next();
fail("FAIL 46 shouldn't be able to use a holdable resultset "
+ "(declared before the savepoint unit) after the rollback "
+ "to savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("XCL16", se);
}
try {// resultset declared within the savepoint unit should be closed
// at this point after the rollback to savepoint
rs2.next();
fail("FAIL 46 shouldn't be able to use a resultset (declared within "
+ "the savepoint unit) after the rollback to savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("XCL16", se);
}
try {// holdable resultset declared within the savepoint unit should
// be closed at this point after the rollback to savepoint
rs2WithHold.next();
fail("FAIL 46 shouldn't be able to use a holdable resultset "
+ "(declared within the savepoint unit) after the rollback "
+ "to savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("XCL16", se);
}
con.rollback();
}
/**
* Test 47 multiple tests for getSavepointId()
*/
public void xtestGetSavepoint() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint();
Savepoint savepoint2 = con.setSavepoint();
savepoint1.getSavepointId();
savepoint2.getSavepointId();
con.releaseSavepoint(savepoint2);
savepoint2 = con.setSavepoint();
savepoint2.getSavepointId();
con.commit();
savepoint2 = con.setSavepoint();
savepoint2.getSavepointId();
con.rollback();
savepoint2 = con.setSavepoint();
savepoint2.getSavepointId();
con.rollback();
}
/**
* Test 48
*/
public void xtestNestedSavepoints() throws SQLException {
Connection con = getConnection();
Savepoint savepoint1 = con.setSavepoint();
Savepoint savepoint2 = con.setSavepoint();
Statement s = createStatement();
try {
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON ROLLBACK"
+ " RETAIN CURSORS");
fail("FAIL 48 shouldn't be able set SQL savepoint nested inside "
+ "JDBC/SQL savepoints");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B002", se);
}
// rollback JDBC savepoint but still can't have SQL savepoint because
// there is still one JDBC savepoint
con.releaseSavepoint(savepoint2);
try {
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON ROLLBACK"
+ " RETAIN CURSORS");
fail("FAIL 48 Should have gotten exception for nested SQL savepoint");
} catch (SQLException se) {
// Expected exception.
assertSQLState("3B002", se);
}
con.releaseSavepoint(savepoint1); // rollback last JDBC savepoint and
// now try SQL savepoint again
s.executeUpdate("SAVEPOINT s1 ON ROLLBACK RETAIN LOCKS ON ROLLBACK "
+ "RETAIN CURSORS");
con.rollback();
}
}