/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.test.it.dxl;
import com.foundationdb.ais.model.AISBuilder;
import com.foundationdb.ais.model.AkibanInformationSchema;
import com.foundationdb.ais.model.Routine;
import com.foundationdb.ais.model.SQLJJar;
import com.foundationdb.ais.model.Sequence;
import com.foundationdb.ais.model.Table;
import com.foundationdb.ais.model.TableName;
import com.foundationdb.ais.model.View;
import com.foundationdb.qp.row.Row;
import com.foundationdb.server.api.dml.scan.NewRow;
import com.foundationdb.server.error.ForeignKeyPreventsDropTableException;
import com.foundationdb.server.error.ForeignConstraintDDLException;
import com.foundationdb.server.error.InvalidOperationException;
import com.foundationdb.server.error.ReferencedSQLJJarException;
import com.foundationdb.server.error.ViewReferencesExist;
import com.foundationdb.server.test.it.ITBase;
import com.foundationdb.server.types.value.ValueSources;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public final class DropSchemaIT extends ITBase {
private void expectTables(String schemaName, String... tableNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : tableNames) {
final Table table = ais.getTable(schemaName, name);
assertNotNull(schemaName + " " + name + " doesn't exist", table);
}
}
private void expectNotTables(String schemaName, String... tableNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : tableNames) {
final Table table = ais.getTable(schemaName, name);
assertNull(schemaName + " " + name + " still exists", table);
}
}
private void expectRoutines(String schemaName, String... routineNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : routineNames) {
final Routine routine = ais.getRoutine(schemaName, name);
assertNotNull(schemaName + " " + name + " doesn't exist", routine);
}
}
private void expectNotRoutines(String schemaName, String... routineNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : routineNames) {
final Routine routine = ais.getRoutine(schemaName, name);
assertNull(schemaName + " " + name + " still exists", routine);
}
}
private void expectSqljJars(String schemaName, String... sqljJarNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : sqljJarNames) {
final SQLJJar sqljJar = ais.getSQLJJar(schemaName, name);
assertNotNull(schemaName + " " + name + " doesn't exist", sqljJar);
}
}
private void expectNotSqljJars(String schemaName, String... sqljJarNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : sqljJarNames) {
final SQLJJar sqljJar = ais.getSQLJJar(schemaName, name);
assertNull(schemaName + " " + name + " still exists", sqljJar);
}
}
private void expectSequences (String schemaName, String... sequenceNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : sequenceNames) {
final Sequence sequence = ais.getSequence(new TableName(schemaName, name));
assertNotNull (schemaName + "." + name + " doesn't exist", sequence);
}
}
private void expectNotSequence (String schemaName, String... sequenceNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : sequenceNames) {
final Sequence sequence = ais.getSequence(new TableName(schemaName, name));
assertNull (schemaName + "." + name + " still exists", sequence);
}
}
private void expectViews(String schemaName, String... viewNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : viewNames) {
final View view = ais.getView(new TableName(schemaName, name));
assertNotNull (schemaName + "." + name + " doesn't exist", view);
}
}
private void expectNotViews(String schemaName, String... viewNames) {
final AkibanInformationSchema ais = ddl().getAIS(session());
for (String name : viewNames) {
final View view = ais.getView(new TableName(schemaName, name));
assertNull (schemaName + "." + name + " still exists", view);
}
}
public void createJarAndRoutine(String jarSchema, String jarName,
String routineSchema, String routineName) throws MalformedURLException {
AISBuilder builder = new AISBuilder();
builder.sqljJar(jarSchema, jarName, new URL("file://ajar.jar"));
ddl().createSQLJJar(session(), builder.akibanInformationSchema().getSQLJJar(jarSchema, jarName));
builder.routine(routineSchema, routineName, "java", Routine.CallingConvention.JAVA);
builder.routineExternalName(routineSchema, routineName, jarSchema, jarName, "className", "method");
ddl().createRoutine(session(), builder.akibanInformationSchema().getRoutine(routineSchema, routineName), true);
}
@After
public void lookForDanglingStorage() throws Exception {
super.lookForDanglingStorage();
}
@Test
public void unknownSchemaIsNoOp() throws InvalidOperationException {
createTable("one", "t", "id int not null primary key");
ddl().dropSchema(session(), "not_a_real_schema");
expectTables("one", "t");
}
@Test
public void singleTable() throws InvalidOperationException {
createTable("one", "t", "id int not null primary key");
ddl().dropSchema(session(), "one");
expectNotTables("one", "t");
}
@Test
public void singleTableCheckData() throws InvalidOperationException {
final int tid1 = createTable("one", "t", "id int not null primary key");
writeRows(row(tid1, 1L), row(tid1, 2L));
ddl().dropSchema(session(), "one");
expectNotTables("one", "t");
// Check for lingering data
final int tid2 = createTable("one", "t", "id int not null primary key");
expectRowCount(tid2, 0);
expectRows(tid2);
}
@Test
public void multipleTables() throws InvalidOperationException {
createTable("one", "a", "id int not null primary key");
createTable("one", "b", "id int not null primary key");
createTable("two", "a", "id int not null primary key");
createTable("two", "b", "id int not null primary key");
ddl().dropSchema(session(), "one");
expectNotTables("one", "a", "b");
expectTables("two", "a", "b");
}
@Test
public void groupedTables() throws InvalidOperationException {
createTable("one", "c", "id int not null primary key");
createTable("one", "o", "id int not null primary key, cid int, grouping foreign key(cid) references c(id)");
createTable("one", "i", "id int not null primary key, oid int, grouping foreign key(oid) references o(id)");
createTable("one", "t", "id int not null primary key");
createTable("two", "c", "id int not null primary key");
ddl().dropSchema(session(), "one");
expectNotTables("one", "c", "o", "i", "t");
expectTables("two", "c");
}
@Test
public void crossSchemaGroupInvalid() throws InvalidOperationException {
createTable("one", "c", "id int not null primary key");
createTable("one", "o", "id int not null primary key, cid int, grouping foreign key(cid) references c(id)");
createTable("two", "i", "id int not null primary key, oid int, grouping foreign key(oid) references one.o(id)");
try {
ddl().dropSchema(session(), "one");
Assert.fail("ForeignConstraintDDLException expected");
} catch(ForeignConstraintDDLException e) {
// expected
}
expectTables("one", "c", "o");
expectTables("two", "i");
}
@Test
public void crossSchemaGroupValid() throws InvalidOperationException {
createTable("one", "c", "id int not null primary key");
createChildTable("one", "o", "one", "c");
createChildTable("two", "i", "one", "c");
ddl().dropSchema(session(), "two");
expectTables("one", "c", "o");
expectNotTables("two", "i");
}
@Test
public void crossSchemaGroupValidCheckData() throws InvalidOperationException {
int cTableId = createTable("one", "c", "id int not null primary key");
writeRow(cTableId, 1);
int oTableId = createTable("two", "o",
"id int not null primary key, oid int, grouping foreign key(oid) references one.c(id)");
writeRow(oTableId, 100, 10);
ddl().dropSchema(session(), "two");
expectTables("one", "c");
expectNotTables("two", "o");
cTableId = getTable("one", "c").getTableId();
expectRows(
cTableId,
row(cTableId, 1));
oTableId = createTable("two", "o",
"id int not null primary key, oid int, grouping foreign key(oid) references one.c(id)");
writeRow(oTableId, 102, 10);
List<Row> newRows = scanAllIndex(getTable("two", "o").getPrimaryKey().getIndex());
assertEquals(newRows.toString(), newRows.size(), 1);
assertEquals(102, ValueSources.toObject(newRows.get(0).value(0)));
expectRows(
oTableId,
row(oTableId, 102, 10));
}
private int createChildTable(String childSchema, String childName, String parentSchema, String parentName) {
return createTable(childSchema, childName,
"id int not null primary key, pid int, grouping foreign key(pid) references " +
parentSchema + "." + parentName + "(id)");
}
@Test
public void crossSchemaGroupValid2() throws InvalidOperationException {
createTable("one", "c", "id int not null primary key");
createChildTable("two", "o", "one", "c");
createChildTable("two", "i", "two", "o");
ddl().dropSchema(session(), "two");
expectTables("one", "c");
expectNotTables("two", "o", "i");
}
@Test
public void crossSchemaGroupValid3() throws InvalidOperationException {
createTable("one", "a", "id int not null primary key");
createChildTable("one", "b", "one", "a");
createChildTable("two", "c", "one", "b");
createChildTable("two", "d", "two", "c");
createChildTable("two", "e", "two", "d");
ddl().dropSchema(session(), "two");
expectTables("one", "a", "b");
expectNotTables("two", "c", "d", "e");
}
@Test
public void crossSchemaForeignKeyInvalid() throws InvalidOperationException {
createTable("one", "c", "id int not null primary key");
createTable("two", "o", "id int not null primary key, cid int, foreign key(cid) references one.c(id)");
try {
ddl().dropSchema(session(), "one");
Assert.fail("ForeignConstraintDDLException expected");
} catch(ForeignKeyPreventsDropTableException e) {
// expected
}
expectTables("one", "c");
expectTables("two", "o");
}
@Test
public void crossSchemaForeignKeyValid() throws InvalidOperationException {
createTable("one", "c", "id int not null primary key");
createTable("two", "o", "id int not null primary key, cid int, foreign key(cid) references one.c(id)");
ddl().dropSchema(session(), "two");
expectTables("one", "c");
expectNotTables("two", "o");
}
@Test
public void dropSchemaSequence() throws InvalidOperationException {
createTable("one", "o", "id int not null PRIMARY KEY generated by default as identity (start with 1)");
createSequence("one", "seq1", "Start with 1 increment by 1 no cycle");
ddl().dropSchema(session(), "one");
expectNotSequence("one", "seq1");
expectNotTables("one", "o");
}
@Test
public void dropViewValidInSchema() throws Exception {
createTable("one", "t1",
"id int not null primary key", "name varchar(128)");
createTable("two", "t2",
"id int not null primary key", "name varchar(128)");
createView("one", "v1",
"SELECT * FROM t1");
createView("two", "v2",
"SELECT * FROM t2");
ddl().dropSchema(session(), "one");
expectNotTables("one", "t1");
expectNotViews("one", "v1");
expectTables("two", "t2");
expectViews("two", "v2");
}
@Test
public void dropViewInvalidOutsideSchema() throws Exception {
createTable("one", "t1",
"id int not null primary key", "name varchar(128)");
createTable("two", "t2",
"id int not null primary key", "name varchar(128)");
createView("one", "crossview",
"SELECT t1.id,t1.name,t2.name AS name2 FROM one.t1 t1, two.t2 t2 WHERE t1.id = t2.id");
try {
ddl().dropSchema(session(), "one");
} catch (ViewReferencesExist ex) {
// expected
}
expectTables("one", "t1");
expectTables("two", "t2");
expectViews("one", "crossview");
}
@Test
public void dropViewsInOrder() throws Exception {
createTable("test", "t1",
"id int not null primary key", "name varchar(128)");
createView("test", "v1",
"SELECT * FROM t1");
createView("test", "v3",
"SELECT * FROM v1");
createView("test", "v2",
"SELECT * FROM v3");
ddl().dropSchema(session(), "test");
expectNotViews("test", "v1", "v2", "v3");
}
@Test
public void dropRoutine() throws Exception {
AISBuilder builder = new AISBuilder();
builder.routine("drop", "f", "javascript", Routine.CallingConvention.SCRIPT_FUNCTION_JSON);
builder.routineDefinition("drop", "f", "function f() { return 3; }");
builder.routine("keep", "p", "javascript", Routine.CallingConvention.SCRIPT_FUNCTION_JSON);
builder.routineDefinition("keep", "p", "function f() { return 8; }");
ddl().createRoutine(session(), builder.akibanInformationSchema().getRoutine("drop", "f"), true);
ddl().createRoutine(session(), builder.akibanInformationSchema().getRoutine("keep", "p"), true);
expectRoutines("drop", "f");
expectRoutines("keep", "p");
ddl().dropSchema(session(), "drop");
expectNotRoutines("drop", "f");
expectRoutines("keep", "p");
}
@Test
public void dropRoutineInJarOfOtherSchema() throws Exception {
createJarAndRoutine("keep", "mixedNuts", "drop", "peanuts");
expectSqljJars("keep", "mixedNuts");
expectRoutines("drop", "peanuts");
ddl().dropSchema(session(), "drop");
expectSqljJars("keep", "mixedNuts");
expectNotRoutines("drop", "peanuts");
}
@Test
public void dropSqljJar() throws Exception {
AISBuilder builder = new AISBuilder();
builder.sqljJar("drop", "grapeJelly", new URL("file://boo.jar"));
ddl().createSQLJJar(session(), builder.akibanInformationSchema().getSQLJJar("drop", "grapeJelly"));
builder.sqljJar("keep", "strawberryJam", new URL("file://far.jar"));
ddl().createSQLJJar(session(), builder.akibanInformationSchema().getSQLJJar("keep", "strawberryJam"));
expectSqljJars("drop", "grapeJelly");
expectSqljJars("keep", "strawberryJam");
ddl().dropSchema(session(), "drop");
expectNotSqljJars("drop", "grapeJelly");
expectSqljJars("keep", "strawberryJam");
}
@Test
public void dropSqljJarWithRoutines() throws Exception {
createJarAndRoutine("drop", "mixedNuts", "drop", "peanuts");
createJarAndRoutine("keep", "treeNuts", "keep", "cashews");
expectSqljJars("drop", "mixedNuts");
expectRoutines("drop", "peanuts");
ddl().dropSchema(session(), "drop");
expectNotSqljJars("drop", "mixedNuts");
expectNotRoutines("drop", "peanut");
expectSqljJars("keep", "treeNuts");
expectRoutines("keep", "cashews");
}
@Test
public void dropSqljJarWithRoutinesInOtherSchemas() throws Exception {
createJarAndRoutine("drop", "mixedNuts", "keep", "cashews");
try {
ddl().dropSchema(session(), "drop");
Assert.fail("Expected exception to be thrown");
} catch (ReferencedSQLJJarException e) {
// expected exception
}
expectSqljJars("drop", "mixedNuts");
expectRoutines("keep", "cashews");
}
@Test
public void createDropRecreateDropAndRestart() throws Exception {
createTable("test2", "customer", "id int not null primary key");
createTable("test2", "order", "name varchar(32)");
createTable("test2", "item", "cost int");
ddl().dropSchema(session(), "test2");
expectNotTables("test2", "customer", "order", "item");
createTable("test2", "order", "id int not null primary key");
createTable("test2", "customer", "id int not null primary key, oid int, grouping foreign key(oid) references \"order\"(id)");
ddl().dropSchema(session(), "test2");
expectNotTables("test2", "customer", "order");
safeRestartTestServices();
expectNotTables("test2", "customer", "order");
}
}