/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.db;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.TimeUnit;
import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
import org.h2.tools.Restore;
import org.h2.util.Task;
/**
* Tests opening and closing a database.
*/
public class TestOpenClose extends TestBase {
private int nextId = 10;
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() throws Exception {
testErrorMessageLocked();
testErrorMessageWrongSplit();
testCloseDelay();
testBackup();
testCase();
testReconnectFast();
deleteDb("openClose");
}
private void testErrorMessageLocked() throws Exception {
if (config.memory) {
return;
}
deleteDb("openClose");
Connection conn;
conn = getConnection("jdbc:h2:" + getBaseDir() + "/openClose;FILE_LOCK=FS");
assertThrows(ErrorCode.DATABASE_ALREADY_OPEN_1, this).getConnection(
"jdbc:h2:" + getBaseDir() + "/openClose;FILE_LOCK=FS;OPEN_NEW=TRUE");
conn.close();
}
private void testErrorMessageWrongSplit() throws Exception {
if (config.memory || config.reopen) {
return;
}
String fn = getBaseDir() + "/openClose2";
if (config.mvStore) {
fn += Constants.SUFFIX_MV_FILE;
} else {
fn += Constants.SUFFIX_PAGE_FILE;
}
FileUtils.delete("split:" + fn);
Connection conn;
String url = "jdbc:h2:split:18:" + getBaseDir() + "/openClose2";
url = getURL(url, true);
conn = DriverManager.getConnection(url);
conn.createStatement().execute("create table test(id int, name varchar) " +
"as select 1, space(1000000)");
conn.close();
FileChannel c = FileUtils.open(fn+".1.part", "rw");
c.position(c.size() * 2 - 1);
c.write(ByteBuffer.wrap(new byte[1]));
c.close();
if (config.mvStore) {
assertThrows(ErrorCode.IO_EXCEPTION_1, this).getConnection(url);
} else {
assertThrows(ErrorCode.IO_EXCEPTION_2, this).getConnection(url);
}
FileUtils.delete("split:" + fn);
}
private void testCloseDelay() throws Exception {
deleteDb("openClose");
String url = getURL("openClose;DB_CLOSE_DELAY=1", true);
String user = getUser(), password = getPassword();
Connection conn = DriverManager.getConnection(url, user, password);
conn.close();
Thread.sleep(950);
long time = System.nanoTime();
while (System.nanoTime() - time < TimeUnit.MILLISECONDS.toNanos(100)) {
conn = DriverManager.getConnection(url, user, password);
conn.close();
}
conn = DriverManager.getConnection(url, user, password);
conn.createStatement().execute("SHUTDOWN");
conn.close();
}
private void testBackup() throws SQLException {
if (config.memory) {
return;
}
deleteDb("openClose");
String url = getURL("openClose", true);
org.h2.Driver.load();
Connection conn = DriverManager.getConnection(url, "sa", "abc def");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE TEST(C CLOB)");
stat.execute("INSERT INTO TEST VALUES(SPACE(10000))");
stat.execute("BACKUP TO '" + getBaseDir() + "/test.zip'");
conn.close();
deleteDb("openClose");
Restore.execute(getBaseDir() + "/test.zip", getBaseDir(), null);
conn = DriverManager.getConnection(url, "sa", "abc def");
stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT * FROM TEST");
rs.next();
assertEquals(10000, rs.getString(1).length());
assertFalse(rs.next());
conn.close();
FileUtils.delete(getBaseDir() + "/test.zip");
}
private void testReconnectFast() throws SQLException {
if (config.memory) {
return;
}
deleteDb("openClose");
String user = getUser(), password = getPassword();
String url = getURL("openClose;DATABASE_EVENT_LISTENER='" +
MyDatabaseEventListener.class.getName() + "'", true);
Connection conn = DriverManager.getConnection(url, user, password);
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR)");
stat.execute("SET MAX_MEMORY_UNDO 100000");
stat.execute("CREATE INDEX IDXNAME ON TEST(NAME)");
stat.execute("INSERT INTO TEST SELECT X, X || ' Data' " +
"FROM SYSTEM_RANGE(1, 1000)");
stat.close();
conn.close();
conn = DriverManager.getConnection(url, user, password);
stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT * FROM DUAL");
if (rs.next()) {
rs.getString(1);
}
rs.close();
stat.close();
conn.close();
conn = DriverManager.getConnection(url, user, password);
stat = conn.createStatement();
// stat.execute("SET DB_CLOSE_DELAY 0");
stat.executeUpdate("SHUTDOWN");
stat.close();
conn.close();
}
private void testCase() throws Exception {
if (config.memory) {
return;
}
org.h2.Driver.load();
deleteDb("openClose");
final String url = getURL("openClose;FILE_LOCK=NO", true);
final String user = getUser(), password = getPassword();
Connection conn = DriverManager.getConnection(url, user, password);
conn.createStatement().execute("drop table employee if exists");
conn.createStatement().execute(
"create table employee(id int primary key, name varchar, salary int)");
conn.close();
// previously using getSize(200, 1000);
// but for Ubuntu, the default ulimit is 1024,
// which breaks the test
int len = getSize(10, 50);
Task[] tasks = new Task[len];
for (int i = 0; i < len; i++) {
tasks[i] = new Task() {
@Override
public void call() throws SQLException {
Connection c = DriverManager.getConnection(url, user, password);
PreparedStatement prep = c
.prepareStatement("insert into employee values(?, ?, 0)");
int id = getNextId();
prep.setInt(1, id);
prep.setString(2, "employee " + id);
prep.execute();
c.close();
}
};
tasks[i].execute();
}
// for(int i=0; i<len; i++) {
// threads[i].start();
// }
for (int i = 0; i < len; i++) {
tasks[i].get();
}
conn = DriverManager.getConnection(url, user, password);
ResultSet rs = conn.createStatement().executeQuery(
"select count(*) from employee");
rs.next();
assertEquals(len, rs.getInt(1));
conn.close();
}
synchronized int getNextId() {
return nextId++;
}
/**
* A database event listener used in this test.
*/
public static final class MyDatabaseEventListener implements
DatabaseEventListener {
@Override
public void exceptionThrown(SQLException e, String sql) {
throw new AssertionError("unexpected: " + e + " sql: " + sql);
}
@Override
public void setProgress(int state, String name, int current, int max) {
String stateName;
switch (state) {
case STATE_SCAN_FILE:
stateName = "Scan " + name + " " + current + "/" + max;
if (current > 0) {
throw new AssertionError("unexpected: " + stateName);
}
break;
case STATE_STATEMENT_START:
break;
case STATE_CREATE_INDEX:
stateName = "Create Index " + name + " " + current + "/" + max;
if (!"SYS:SYS_ID".equals(name)) {
throw new AssertionError("unexpected: " + stateName);
}
break;
case STATE_RECOVER:
stateName = "Recover " + current + "/" + max;
break;
default:
stateName = "?";
}
// System.out.println(": " + stateName);
}
@Override
public void closingDatabase() {
// nothing to do
}
@Override
public void init(String url) {
// nothing to do
}
@Override
public void opened() {
// nothing to do
}
}
}