blob: 88e1fbdef5669e973dbe195039db4c7cadae1cc6 [file] [log] [blame]
/*
* 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.SQLException;
import java.sql.Statement;
import java.util.Properties;
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;
/**
* Tests the lazy creation functionality of default schema: the schema
* is only first created when the first database object is created in
* the schema.
*/
public class LazyDefaultSchemaCreationTest extends BaseJDBCTestCase {
final private static String LOCK_TIMEOUT = "40XL1";
/**
* Creates a new {@code LazyDefaultSchemaCreationTest} instance.
*
* @param name the name of the test
*/
public LazyDefaultSchemaCreationTest(String name) {
super(name);
}
/**
* Reproduces hang seen in DERBY-48
*/
public void testDerby48testNewSchemaHang () throws SQLException
{
Connection c1 = openUserConnection("newuser");
c1.setAutoCommit(false);
Statement s1 = c1.createStatement();
// Will auto-create schema NEWUSER:
s1.executeUpdate("create table t1(i int)");
s1.close();
// DERBY-48: The next connect causes a hang on write lock the
// new schema row being created by c1 that is not yet
// committed if the fix for DERBY-48 is not yet in place.
// The fix makes the the auto-create happen in a nested transaction
// which commit immediately, so the hang should not be present.
Connection c2 = null;
try {
c2 = openUserConnection("newuser");
} catch (SQLException e) {
if (e.getSQLState().equals(LOCK_TIMEOUT)) {
c1.rollback();
c1.close();
fail("DERBY-48 still seen", e);
} else {
throw e;
}
}
c1.rollback();
// Since the auto-create happened in a nested transaction
// which has committed, the schema should still be around
// after the rollback. Note that this is a side-effect of the
// fix for DERBY-48, not required behavior for SQL, but it is
// user visible behavior, so we test it here to make sure that
// patch works as intended:
JDBC.assertSingleValueResultSet(
c1.createStatement().executeQuery(
"select schemaname from sys.sysschemas " +
"where schemaname='NEWUSER'"),
"NEWUSER");
c1.rollback();
c1.close();
c2.close();
}
/**
* Test that we recover from self locking in the auto-create
* nested transaction (cf solution for DERBY-48).
*/
public void testDerby48SelfLockingRecovery () throws SQLException
{
Connection c1 = openUserConnection("newuser");
c1.setAutoCommit(false);
c1.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
Statement s1 = c1.createStatement();
// Set read locks in parent transaction
s1.executeQuery("select count(*) from sys.sysschemas");
// ..which conflicts with the auto-create in a subtransaction
// which will self-lock here, but should recover to try again
// in outer transaction:
s1.executeUpdate("create table t1(i int)");
JDBC.assertSingleValueResultSet(
s1.executeQuery(
"select schemaname from sys.sysschemas " +
"where schemaname='NEWUSER'"),
"NEWUSER");
c1.rollback();
// Since the fallback does the auto-create of the schema in
// the outer transaction, a rollback will remove it:
JDBC.assertEmpty(
s1.executeQuery
("select * from sys.sysschemas where schemaname='NEWUSER'"));
c1.rollback();
}
/**
* Test that we do get to see the self locking in the auto-create
* nested transaction (cf solution for DERBY-48) when deadlock
* detection is on, i.e. 40XL2 (LOCK_TIMEOUT_LOG) rather than
* 40XL1 (LOCK_TIMEOUT) happens.
*
* After fix for DERBY-5564 LOCK_TIMEOUT will be returned whether
* diagnostics are on or not.
*/
public void testDerby48SelfLockingRecoveryDeadlockDetectionOn ()
throws SQLException
{
Connection c1 = openUserConnection("newuser");
c1.setAutoCommit(false);
c1.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
Statement s1 = c1.createStatement();
// Set read locks in parent transaction
s1.executeQuery("select count(*) from sys.sysschemas");
// ..which conflicts with the auto-create in a subtransaction
// which will self-lock here, but should throw now:
// in outer transaction:
try {
s1.executeUpdate("create table t1(i int)");
fail("Expected exception " + LOCK_TIMEOUT);
} catch (SQLException e) {
assertSQLState("Expected state: ", LOCK_TIMEOUT, e);
}
JDBC.assertEmpty(
s1.executeQuery
("select * from sys.sysschemas where schemaname='NEWUSER'"));
c1.rollback();
}
/**
* Test that the timeout lock diagnostics do not create an
* infinite recursion as in DERBY-3678 (although that particular
* use case will not cause an infinite recursion after the fix to
* DERBY-48). The scenario in this test case does create the
* infinite recursion prior to the fix of DERBY-3678, however.
*
* After fix for DERBY-5564 LOCK_TIMEOUT SQL state should be returned
* for a lock timeout whether diagnostics are on or not.
*/
public void testDerby3678 ()
throws SQLException
{
Connection c1 = openUserConnection("newuser");
Connection c2 = null;
c1.setAutoCommit(false);
Statement s1 = c1.createStatement();
// set locks in connection 1:
s1.executeUpdate("create schema newuser");
s1.executeUpdate("create table t(i int)");
// ..which conflicts with the next connect
try {
c2 = openUserConnection("newuser");
fail("Expected exception " + LOCK_TIMEOUT);
} catch (SQLException e) {
assertSQLState("Expected state: ", LOCK_TIMEOUT, e);
} finally {
c1.rollback();
}
}
/**
* Test that implicit schema creation of other schemas besides
* the initial default schema is still transactional.
*/
public void testOtherImplicitSchemaCreation () throws SQLException
{
Connection c1 = openUserConnection("newuser");
c1.setAutoCommit(false);
Statement s1 = c1.createStatement();
// Will auto-create schema OTHERSCHEMA:
s1.executeUpdate("create table otherschema.t1(i int)");
s1.close();
JDBC.assertSingleValueResultSet(
c1.createStatement().executeQuery(
"select schemaname from sys.sysschemas " +
"where schemaname='OTHERSCHEMA'"),
"OTHERSCHEMA");
c1.rollback();
JDBC.assertEmpty(
c1.createStatement().executeQuery(
"select schemaname from sys.sysschemas " +
"where schemaname='OTHERSCHEMA'"));
c1.rollback();
c1.close();
}
public void testDerby3043CheckConstraint() throws SQLException
{
// Demonstrate the DERBY-3043 workaround: if the table name is
// schema-qualified, check constraints do not cause a problem,
// and the named schema is automatically created if it does
// not yet exist:
Connection c0 = openUserConnection("frogs");
Statement s0 = c0.createStatement();
JDBC.assertEmpty( s0.executeQuery
("select * from sys.sysschemas where schemaname='FROGS'"));
JDBC.assertEmpty( s0.executeQuery
("select * from sys.sysschemas where schemaname='NOSUCH'"));
// A simple example, which should work whether or not the
// DERBY-3043 fix is in place
s0.executeUpdate("create table frogs.users2(username varchar(16) " +
"CHECK(LENGTH(username)>7))");
// Demonstrate that any schema is lazy-created, not just the
// default schema which matches the username:
s0.executeUpdate("create table nosuch.users(username varchar(16) " +
"CHECK(LENGTH(username)>7))");
// Schemas FROGS and NOSUCH have been lazy-created:
JDBC.assertSingleValueResultSet( s0.executeQuery(
"select schemaname from sys.sysschemas " +
"where schemaname='FROGS'"),
"FROGS");
JDBC.assertSingleValueResultSet( s0.executeQuery(
"select schemaname from sys.sysschemas " +
"where schemaname='NOSUCH'"),
"NOSUCH");
c0.close();
// Now verify that the test cases from DERBY-3043 pass:
Connection c1 = openUserConnection("blogs");
Statement s1 = c1.createStatement();
// At the beginning, the schema 'blogs' does not exist.
JDBC.assertEmpty( s1.executeQuery
("select * from sys.sysschemas where schemaname='BLOGS'"));
// Should work, but without the DERBY-3043 fix will get a
// "Schema blogs does not exist" error
s1.executeUpdate("create table users(username varchar(16) " +
"CHECK(LENGTH(username)>7))");
// Another slightly more complicated example, which requires
// the DERBY-3043 fix again to work.
s1.executeUpdate("CREATE TABLE BLOGSCOM__BLOGS__USERS(" +
"PK INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY," +
"username VARCHAR(16) NOT NULL " +
" CONSTRAINT BLOGSCOM__BLOGS__USERS_UNIQUE_username UNIQUE " +
" CONSTRAINT BLOGSCOM__BLOGS__USERS_PASSWORD_username " +
" CHECK(LENGTH(username)>7)," +
"password VARCHAR (32672) NOT NULL , " +
"PRIMARY KEY(PK))");
// Schema BLOGS should have been lazy-created:
JDBC.assertSingleValueResultSet( s1.executeQuery(
"select schemaname from sys.sysschemas " +
"where schemaname='BLOGS'"),
"BLOGS");
c1.close();
}
protected void tearDown() throws Exception {
try {
createStatement().executeUpdate("drop schema newuser restrict");
} catch (SQLException e) {
// If not created by the fixture:
assertSQLState("Expected state: ", "42Y07", e);
}
super.tearDown();
}
public static Test suite() {
BaseTestSuite suite =
new BaseTestSuite("LazyDefaultSchemaCreationTest");
BaseTestSuite[] suites = {
new BaseTestSuite("LazyDefaultSchemaCreationTest:embedded"),
new BaseTestSuite("LazyDefaultSchemaCreationTest:clientServer") };
for (int i=0; i < 2; i++) {
suites[i].addTest(DatabasePropertyTestSetup.setLockTimeouts
(new LazyDefaultSchemaCreationTest
("testDerby48testNewSchemaHang"),2,1));
suites[i].addTest(DatabasePropertyTestSetup.setLockTimeouts
(new LazyDefaultSchemaCreationTest
("testDerby48SelfLockingRecovery"),2,1));
Properties p = new Properties();
p.setProperty("derby.locks.deadlockTrace", "true");
suites[i].addTest
(DatabasePropertyTestSetup.setLockTimeouts
(new DatabasePropertyTestSetup
(new LazyDefaultSchemaCreationTest
("testDerby48SelfLockingRecoveryDeadlockDetectionOn"),
p, false),
2, // deadlock timeout
1)); // wait timeout
suites[i].addTest
(DatabasePropertyTestSetup.setLockTimeouts
(new DatabasePropertyTestSetup
(new LazyDefaultSchemaCreationTest
("testDerby3678"),
p, false),
2, // deadlock timeout
1)); // wait timeout
suites[i].addTest(new CleanDatabaseTestSetup(
new LazyDefaultSchemaCreationTest(
"testDerby3043CheckConstraint")));
if (i == 0) {
suite.addTest(suites[i]);
} else {
suite.addTest(
TestConfiguration.clientServerDecorator(suites[i]));
}
}
return suite;
}
}