blob: 8bc7c0076b6ba63ef361b7bd8a9604b4c56beb4d [file] [log] [blame]
/*
* Derby - Class org.apache.derbyTesting.functionTests.tests.memory.Derby5730Test
*
* 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.memory;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.SpawnedProcess;
/**
* Regression test for DERBY-5730. Repeatedly boot and shut down a database.
* In between call a function in the SYSFUN schema, a different one each time.
* Without the fix, a reference to the currently running database instance will
* be leaked for each iteration, and eventually an OutOfMemoryError is raised.
*/
public class Derby5730Test extends BaseTestCase {
public Derby5730Test(String name) {
super(name);
}
public static Test suite() {
// The test case uses DriverManager, so require JDBC 3.0 or higher.
if (JDBC.vmSupportsJDBC3()) {
return new BaseTestSuite(Derby5730Test.class);
}
return new BaseTestSuite("Derby5730Test - skipped");
}
/**
* Test case for DERBY-5730. The memory leak is only reproduced if the
* test case runs with capped heap size and the SYSFUN functions have not
* been called previously in the same JVM process. Spawn a new process to
* satisfy those requirements.
*/
public void testLeak() throws IOException {
String[] cmd = {"-Xmx16M", getClass().getName()};
SpawnedProcess sp = new SpawnedProcess(execJavaCmd(cmd), "DERBY-5730");
if (sp.complete() != 0) {
fail(sp.getFailMessage("Process failed"));
}
}
private final static Integer ZERO = Integer.valueOf("0");
private final static Integer ONE = Integer.valueOf("1");
private final static Integer TWO = Integer.valueOf("2");
/**
* These are the functions in the SYSFUN schema. The second value in each
* row tells how many arguments the function takes.
*/
private final static Object[][] FUNCTIONS = {
{"ACOS", ONE},
{"ASIN", ONE},
{"ATAN", ONE},
{"ATAN2", TWO},
{"COS", ONE},
{"SIN", ONE},
{"TAN", ONE},
{"PI", ZERO},
{"DEGREES", ONE},
{"RADIANS", ONE},
{"LN", ONE},
{"LOG", ONE},
{"LOG10", ONE},
{"EXP", ONE},
{"CEIL", ONE},
{"CEILING", ONE},
{"FLOOR", ONE},
{"SIGN", ONE},
{"RANDOM", ZERO},
{"RAND", ONE},
{"COT", ONE},
{"COSH", ONE},
{"SINH", ONE},
{"TANH", ONE},
};
/**
* Boot and repeatedly reboot a database, calling SYSFUN functions in
* between. Eventually runs out of memory if DERBY-5730 is not fixed.
* Must run with capped memory size (-Xmx16M) to expose the memory leak.
*/
public static void main(String[] args) throws Exception {
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
for (int i = 0; i < FUNCTIONS.length; i++) {
Connection c = DriverManager.getConnection(
"jdbc:derby:memory:derby5730;create=true");
prepareFunction(c,
(String) FUNCTIONS[i][0],
((Integer) FUNCTIONS[i][1]).intValue());
growDatabaseFootprint(c);
c.close();
try {
DriverManager.getConnection(
"jdbc:derby:memory:derby5730;shutdown=true");
fail("Shutdown should throw exception");
} catch (SQLException sqle) {
BaseJDBCTestCase.assertSQLState("08006", sqle);
}
}
}
/**
* Prepare a call to a function. Close the statement once it is prepared.
* Before the bug was fixed, preparing a call to a function in SYSFUN that
* hadn't been prepared in the same JVM process, would leak a reference to
* the current database instance.
*/
private static void prepareFunction(Connection c, String name, int args)
throws SQLException {
StringBuffer sql = new StringBuffer("VALUES ");
sql.append(name);
sql.append('(');
for (int i = 0; i < args; i++) {
if (i > 0) sql.append(',');
sql.append('?');
}
sql.append(')');
String sqlText = sql.toString();
System.out.println(sqlText);
PreparedStatement ps = c.prepareStatement(sqlText);
ps.close();
}
/**
* Perform some database operations so that the internal structures of
* the database instance (caches, for example) are filled, and the memory
* footprint of the database instance grows. This is done to make the
* test case run out of memory faster.
*/
private static void growDatabaseFootprint(Connection c)
throws SQLException {
DatabaseMetaData dmd = c.getMetaData();
JDBC.assertDrainResults(dmd.getColumns(null, "%", "%", "%"));
JDBC.assertDrainResults(dmd.getTables(null, "%", "%", null));
}
}