1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
|
/*
* Class org.apache.derbyTesting.functionTests.tests.lang.ErrorMessageTest
*
* 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.lang;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import junit.framework.Test;
import org.apache.derbyTesting.functionTests.util.Barrier;
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;
public class ErrorMessageTest extends BaseJDBCTestCase {
public ErrorMessageTest(String name) {
super(name);
}
/**
* Create a test suite with all the tests in this class. The tests are only
* run in embedded mode since they test the messages generated by the
* embedded driver.
*/
public static Test suite() {
BaseTestSuite suite = new BaseTestSuite("ErrorMessageTest");
if (JDBC.vmSupportsJSR169()) {
// Foundation 1.1 doesn't support the regex classes. Return an
// empty test suite.
return suite;
}
// Set a short wait timeout so that the expected timeout exception
// is thrown faster.
suite.addTest(DatabasePropertyTestSetup.setLockTimeouts(
new ErrorMessageTest("testWaitTimeout"), 1, 2));
// Set a short deadlock timeout so that the expected deadlock is
// found faster. Keep the lock timeout high to prevent false lock
// timeouts from being reported because the deadlock detector cannot
// resolve the deadlock fast enough (DERBY-6001).
suite.addTest(DatabasePropertyTestSetup.setLockTimeouts(
new ErrorMessageTest("testDeadlockTimeout"), 1, 60));
// testWaitTimeout wants more detailed error messages on timeout.
Test test = DatabasePropertyTestSetup.singleProperty(
suite, "derby.locks.deadlockTrace", "true");
// create some data to work on
return new CleanDatabaseTestSetup(test) {
protected void decorateSQL(Statement s) throws SQLException {
s.executeUpdate("create table t (id int primary key, " +
"text varchar(10))");
s.executeUpdate("insert into t (id) values 1, 2");
}
};
}
/**
* Test that a wait timeout prints the lock table correctly when the
* <code>derby.locks.deadlockTrace</code> property is set. DERBY-2817
*
* After fix for DERBY-5564, the sql state for a lock timeout will be
* the same whether diagnostics are on or not (ie. 40XL1).
*/
public void testWaitTimeout() throws SQLException {
getConnection().setAutoCommit(false);
Statement s = createStatement();
assertUpdateCount(s, 1, "update t set text='xxx' where id=1");
Connection c2 = openDefaultConnection();
Statement s2 = c2.createStatement();
try {
// the first transaction has locked row with id=1, so this query
// will time out
JDBC.assertDrainResults(
s2.executeQuery("select * from t where id=1"));
fail("Expected lock timeout");
} catch (SQLException e) {
assertSQLState("Not a timeout", "40XL1", e);
// check that information about the victim is printed
String[] msg = e.getMessage().split("\n");
assertEquals("*** The following row is the victim ***", msg[4]);
assertEquals("*** The above row is the victim ***", msg[6]);
String[] victim = msg[5].split(" *\\|");
assertTrue("Invalid XID string: " + victim[0],
victim[0].matches("\\d+"));
assertEquals("Victim should be a row lock", "ROW", victim[1]);
assertEquals("Victim should be a shared lock", "S", victim[2]);
assertEquals("Victim should be waiting", "WAIT", victim[5]);
// check that the rest of the lock table is dumped
boolean locksDumped = false;
for (int i = 7; i < msg.length - 1; i++) {
String[] tokens = msg[i].split(" *\\|");
assertTrue("Invalid XID string: " + tokens[0],
tokens[0].matches("\\d+"));
assertTrue("Unexpected lock type: " + tokens[1],
tokens[1].matches("ROW|TABLE"));
assertTrue("Unexpected lock mode: " + tokens[2],
tokens[2].matches("S|X|IX|IS"));
assertEquals("Expected lock to be granted", "GRANT", tokens[5]);
locksDumped = true;
}
assertTrue("No locks dumped", locksDumped);
}
s.close();
s2.close();
c2.close();
}
/**
* Test that the error message from a deadlock timeout contains information
* about the locks involved in the deadlock. DERBY-2817
*/
public void testDeadlockTimeout()
throws SQLException, InterruptedException {
setAutoCommit(false);
// Make the main transaction (T1) lock row 1 exclusively
Statement s = createStatement();
assertUpdateCount(s, 1, "update t set text='xxx' where id=1");
// Start another transaction (T2) that locks row 2 exclusively
Connection c2 = openDefaultConnection();
c2.setAutoCommit(false);
Statement s2 = c2.createStatement();
assertUpdateCount(s2, 1, "update t set text='yyy' where id=2");
// Prepare statements for T1 to lock row 2 (shared), and for T2 to
// lock row 1 (shared).
PreparedStatement ps1 = prepareStatement("select * from t where id=2");
final PreparedStatement ps2 =
c2.prepareStatement("select * from t where id=1");
// Create a barrier for the two threads to synchronize.
final Barrier barrier = new Barrier(2);
final SQLException[] holder = new SQLException[2];
final Throwable[] unexpected = new Throwable[1];
Thread t = new Thread(new Runnable() {
public void run() {
try {
// Let the main thread know the helper thread has
// started. The race for the locks can start.
barrier.await();
// This statement will be blocked because T1 holds
// an exclusive lock on the row we want.
JDBC.assertDrainResults(ps2.executeQuery());
} catch (SQLException e) {
holder[0] = e;
} catch (Throwable t) {
unexpected[0] = t;
}
}
});
t.start();
// Wait until the helper thread has started. Once the call returns,
// both threads are ready, and the race for the locks can start.
barrier.await();
// This statement will be blocked because T2 holds an exclusive lock
// on the row we want. So now we have T1 waiting for T2, and T2 waiting
// for T1, and one of the transactions should be terminated because of
// the deadlock.
try {
JDBC.assertDrainResults(ps1.executeQuery());
} catch (SQLException e) {
holder[1] = e;
}
// Wait for the helper thread to complete.
t.join();
// If the helper thread failed with something other than an
// SQLException, report it.
if (unexpected[0] != null) {
fail("Helper thread failed unexpectedly", unexpected[0]);
}
// Check that exactly one of the threads failed, and that the failure
// was caused by a deadlock. It is not deterministic which of the two
// threads will be terminated.
assertFalse("No deadlock", holder[0] == null && holder[1] == null);
if (holder[0] != null && holder[1] != null) {
// Both threads failed. Print some more information to the log
// so we can see what's going on.
printStackTrace(holder[0]);
printStackTrace(holder[1]);
fail("Only one of the waiters should be aborted");
}
SQLException deadlock = holder[0] == null ? holder[1] : holder[0];
assertSQLState("Not a deadlock", "40001", deadlock);
String[] lines = deadlock.getMessage().split("\n");
assertEquals("Unexpected number of lines in message", 8, lines.length);
Pattern[] patterns = new Pattern[] {
Pattern.compile("Lock : ROW, T, \\(\\d+,\\d+\\)"),
Pattern.compile(" *Waiting XID : \\{\\d+, S\\} , APP, " +
"select \\* from t where id=(1|2)"),
Pattern.compile(" *Granted XID : \\{\\d+, X\\} *"),
};
// check the descriptions of the two locks involved in the deadlock
for (int i = 0; i < patterns.length * 2; i++) {
String line = lines[i+1];
Matcher m = patterns[i%patterns.length].matcher(line);
assertTrue("mismatch: " + line, m.matches());
}
s.close();
s2.close();
c2.rollback();
c2.close();
}
}
|