/** * 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.ais.util; import com.foundationdb.ais.model.*; import com.foundationdb.ais.model.ForeignKey.Action; import com.foundationdb.ais.model.aisb2.AISBBasedBuilder; import com.foundationdb.ais.model.aisb2.NewAISBuilder; import com.foundationdb.ais.model.aisb2.NewTableBuilder; import com.foundationdb.ais.util.TableChange.ChangeType; import com.foundationdb.server.types.common.types.TypesTranslator; import com.foundationdb.server.types.mcompat.mtypes.MTypesTranslator; import org.junit.Test; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import static com.foundationdb.ais.util.ChangedTableDescription.ParentChange; import static com.foundationdb.ais.util.TableChangeValidator.ChangeLevel; import static com.foundationdb.ais.util.TableChangeValidatorException.*; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; public class TableChangeValidatorTest { private static final String SCHEMA = "test"; private static final String TABLE = "t"; private static final TableName TABLE_NAME = new TableName(SCHEMA, TABLE); private static final String EMPTY_LIST_STR = Collections.emptyList().toString(); private static final String EMPTY_MAP_STR = Collections.emptyMap().toString(); private static final String[] NO_INDEX_CHANGE = { EMPTY_LIST_STR, EMPTY_MAP_STR, EMPTY_MAP_STR }; private static final String NO_IDENTITY_CHANGE = ""; private final List<TableChange> NO_CHANGES = Collections.emptyList(); private final List<TableChange> AUTO_CHANGES = new ArrayList<>(); private final TypesTranslator typesTranslator = MTypesTranslator.INSTANCE; private NewTableBuilder builder(TableName name) { return AISBBasedBuilder.create(SCHEMA, typesTranslator).table(name); } private Table table(NewAISBuilder builder) { AkibanInformationSchema ais = builder.ais(); assertEquals("User table count", 1, ais.getTables().size()); return ais.getTables().values().iterator().next(); } private Table table(NewAISBuilder builder, TableName tableName) { Table table = builder.ais().getTable(tableName); assertNotNull("Found table: " + tableName, table); return table; } private static TableChangeValidator validate(Table t1, Table t2, List<TableChange> columnChanges, List<TableChange> indexChanges, ChangeLevel expectedChangeLevel) { return validate(t1, t2, columnChanges, indexChanges, expectedChangeLevel, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE, "PRIMARY", "PRIMARY")), false, false, NO_INDEX_CHANGE, NO_IDENTITY_CHANGE); } private static TableChangeValidator validate(Table t1, Table t2, List<TableChange> columnChanges, List<TableChange> indexChanges, ChangeLevel expectedChangeLevel, List<String> expectedChangedTables) { return validate(t1, t2, columnChanges, indexChanges, expectedChangeLevel, expectedChangedTables, false, false, NO_INDEX_CHANGE, NO_IDENTITY_CHANGE); } private static TableChangeValidator validate(Table t1, Table t2, List<TableChange> columnChanges, List<TableChange> indexChanges, ChangeLevel expectedChangeLevel, List<String> expectedChangedTables, boolean expectedParentChange, boolean expectedPrimaryKeyChange, String[] expectedGIChanges, String expectedIdentityChange) { TableChangeValidator validator = new TableChangeValidator(t1, t2, columnChanges, indexChanges); validator.compare(); assertEquals("Final change level", expectedChangeLevel, validator.getFinalChangeLevel()); assertEquals("Parent changed", expectedParentChange, validator.isParentChanged()); assertEquals("Primary key changed", expectedPrimaryKeyChange, validator.isPrimaryKeyChanged()); assertEquals("Changed tables", expectedChangedTables.toString(), validator.getState().descriptions.toString()); Collections.sort(validator.getState().droppedGI); assertEquals("Dropped group index", expectedGIChanges[0], validator.getState().droppedGI.toString()); assertEquals("Recreate group index", expectedGIChanges[1], validator.getState().affectedGI.toString()); assertEquals("Rebuild group index", expectedGIChanges[2], validator.getState().dataAffectedGI.toString()); assertEquals("Unmodified changes", "[]", validator.getUnmodifiedChanges().toString()); assertEquals("Changed identity", expectedIdentityChange, identityChangeDesc(validator.getState().descriptions)); return validator; } private static Map<String,String> map(String... pairs) { assertTrue("Even number of pairs", (pairs.length % 2) == 0); Map<String,String> map = new TreeMap<>(); for(int i = 0; i < pairs.length; i += 2) { map.put(pairs[i], pairs[i+1]); } return map; } private static String changeDesc(TableName oldName, TableName newName, boolean newGroup, ParentChange parentChange, String... preservedIndexPairs) { return ChangedTableDescription.toString(oldName, newName, newGroup, parentChange, map(preservedIndexPairs)); } private static String identityChangeDesc(Collection<ChangedTableDescription> tableChanges) { StringBuilder str = new StringBuilder(); for (ChangedTableDescription change : tableChanges) { if (!change.getDroppedSequences().isEmpty()) { str.append("-").append(change.getDroppedSequences()); } if (!change.getIdentityAdded().isEmpty()) { str.append("+").append(change.getIdentityAdded()); } } return str.toString(); } // // Table // @Test public void sameTable() { Table t = table(builder(TABLE_NAME).colBigInt("id").pk("id")); validate(t, t, NO_CHANGES, NO_CHANGES, ChangeLevel.NONE); } @Test public void unchangedTable() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); validate(t1, t2, NO_CHANGES, NO_CHANGES, ChangeLevel.NONE); } @Test public void changeOnlyTableName() { TableName name2 = new TableName("x", "y"); Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(name2).colBigInt("id").pk("id")); validate(t1, t2, NO_CHANGES, NO_CHANGES, ChangeLevel.METADATA, asList(changeDesc(TABLE_NAME, name2, false, ParentChange.NONE, "PRIMARY", "PRIMARY"))); } @Test public void changeDefaultCharset() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); t1.setCharsetAndCollation("utf8", "ucs_binary"); Table t2 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); t1.setCharsetAndCollation("utf16", "ucs_binary"); validate(t1, t2, NO_CHANGES, NO_CHANGES, ChangeLevel.METADATA); } @Test public void changeDefaultCollation() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); t1.setCharsetAndCollation("utf8", "ucs_binary"); Table t2 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); t1.setCharsetAndCollation("utf8", "en_us_ci"); validate(t1, t2, NO_CHANGES, NO_CHANGES, ChangeLevel.METADATA); } // // Column // @Test public void addColumn() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); validate(t1, t2, asList(TableChange.createAdd("x")), NO_CHANGES, ChangeLevel.TABLE); } @Test public void addIdentityColumn() { final TableName SEQ_NAME = new TableName(SCHEMA, "seq-1"); Table t1 = table(builder(TABLE_NAME).colBigInt("x")); Table t2 = table(builder(TABLE_NAME).colBigInt("x").colBigInt("id").pk("id").sequence(SEQ_NAME.getTableName())); t2.getColumn("id").setIdentityGenerator(t2.getAIS().getSequence(SEQ_NAME)); t2.getColumn("id").setDefaultIdentity(true); validate(t1, t2, asList(TableChange.createDrop(Column.ROW_ID_NAME),TableChange.createAdd("id")), asList(TableChange.createModify(Index.PRIMARY, Index.PRIMARY)), ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE)), false, true, NO_INDEX_CHANGE, "-[test.t___row_id_seq]+[id]"); } @Test public void dropColumn() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); validate(t1, t2, asList(TableChange.createDrop("x")), NO_CHANGES, ChangeLevel.TABLE); } @Test public void modifyColumnDataType() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("y").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colString("y", 32).pk("id")); validate(t1, t2, asList(TableChange.createModify("y", "y")), NO_CHANGES, ChangeLevel.TABLE); } @Test public void modifyColumnName() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("y").pk("id")); validate(t1, t2, asList(TableChange.createModify("x", "y")), NO_CHANGES, ChangeLevel.METADATA); } @Test public void modifyColumnNotNullToNull() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x", false).pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x", true).pk("id")); validate(t1, t2, asList(TableChange.createModify("x", "x")), NO_CHANGES, ChangeLevel.METADATA); } @Test public void modifyColumnNullToNotNull() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x", true).pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x", false).pk("id")); validate(t1, t2, asList(TableChange.createModify("x", "x")), NO_CHANGES, ChangeLevel.METADATA_CONSTRAINT); } @Test public void modifyAddGeneratedBy() { final TableName SEQ_NAME = new TableName(SCHEMA, "seq-1"); Table t1 = table(builder(TABLE_NAME).colInt("id", false).pk("id")); Table t2 = table(builder(TABLE_NAME).colInt("id", false).pk("id").sequence(SEQ_NAME.getTableName())); t2.getColumn("id").setIdentityGenerator(t2.getAIS().getSequence(SEQ_NAME)); t2.getColumn("id").setDefaultIdentity(true); validate(t1, t2, asList(TableChange.createModify("id", "id")), NO_CHANGES, ChangeLevel.METADATA, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE, "PRIMARY", "PRIMARY")), false, false, NO_INDEX_CHANGE, "+[id]"); } @Test public void modifyDropGeneratedBy() { final TableName SEQ_NAME = new TableName(SCHEMA, "seq-1"); Table t1 = table(builder(TABLE_NAME).colInt("id", false).pk("id").sequence(SEQ_NAME.getTableName())); t1.getColumn("id").setIdentityGenerator(t1.getAIS().getSequence(SEQ_NAME)); t1.getColumn("id").setDefaultIdentity(true); Table t2 = table(builder(TABLE_NAME).colInt("id", false).pk("id")); validate(t1, t2, asList(TableChange.createModify("id", "id")), NO_CHANGES, ChangeLevel.METADATA, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE, "PRIMARY", "PRIMARY")), false, false, NO_INDEX_CHANGE, "-[test.seq-1]"); } @Test public void modifyColumnAddDefault() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x", false).colInt("c1", true).pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x", false).colInt("c1", true).pk("id")); t2.getColumn("c1").setDefaultValue("42"); validate(t1, t2, asList(TableChange.createModify("c1", "c1")), NO_CHANGES, ChangeLevel.METADATA); } @Test public void modifyColumnDropDefault() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x", false).colInt("c1", true).pk("id")); t1.getColumn("c1").setDefaultValue("42"); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x", false).colInt("c1", true).pk("id")); validate(t1, t2, asList(TableChange.createModify("c1", "c1")), NO_CHANGES, ChangeLevel.METADATA); } // // Column (negative) // @Test(expected=UndeclaredColumnChangeException.class) public void addColumnUnspecified() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); validate(t1, t2, NO_CHANGES, NO_CHANGES, null); } @Test(expected=UnchangedColumnNotPresentException.class) public void dropColumnUnspecified() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); validate(t1, t2, NO_CHANGES, NO_CHANGES, null); } @Test(expected=DropColumnNotPresentException.class) public void dropColumnUnknown() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); validate(t1, t2, asList(TableChange.createDrop("x")), NO_CHANGES, null); } @Test public void modifyColumnNotChanged() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); TableChangeValidator tcv = new TableChangeValidator(t1, t2, asList(TableChange.createModify("x", "x")), NO_CHANGES); tcv.compare(); assertEquals("Final change level", ChangeLevel.NONE, tcv.getFinalChangeLevel()); assertEquals("Unmodified change count", 1, tcv.getUnmodifiedChanges().size()); } @Test(expected=ModifyColumnNotPresentException.class) public void modifyColumnUnknown() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); validate(t1, t2, asList(TableChange.createModify("y", "y")), NO_CHANGES, null); } @Test(expected=UndeclaredColumnChangeException.class) public void modifyColumnUnspecified() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colString("x", 32).pk("id")); validate(t1, t2, NO_CHANGES, NO_CHANGES, null); } // // Index // @Test public void addIndex() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("x", "x").pk("id")); validate(t1, t2, NO_CHANGES, asList(TableChange.createAdd("x")), ChangeLevel.INDEX); } @Test public void dropIndex() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("x", "x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); validate(t1, t2, NO_CHANGES, asList(TableChange.createDrop("x")), ChangeLevel.INDEX); } @Test public void modifyIndexedColumn() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").colBigInt("y").key("k", "x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").colBigInt("y").key("k", "y").pk("id")); validate(t1, t2, NO_CHANGES, asList(TableChange.createModify("k", "k")), ChangeLevel.INDEX); } @Test public void modifyPKColumn() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").colBigInt("y").pk("x")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").colBigInt("y").pk("y")); validate(t1, t2, NO_CHANGES, asList(TableChange.createModify(Index.PRIMARY, Index.PRIMARY)), ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE)), false, true, NO_INDEX_CHANGE, NO_IDENTITY_CHANGE); } @Test public void modifyIndexedType() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("x", "x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colString("x", 32).key("x", "x").pk("id")); validate(t1, t2, asList(TableChange.createModify("x", "x")), asList(TableChange.createModify("x", "x")), ChangeLevel.TABLE); } @Test public void modifyIndexName() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("a", "x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("b", "x").pk("id")); validate(t1, t2, NO_CHANGES, asList(TableChange.createModify("a", "b")), ChangeLevel.METADATA); } // // Index (negative) // @Test public void addIndexUnspecified() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("x", "x").pk("id")); validate(t1, t2, NO_CHANGES, AUTO_CHANGES, ChangeLevel.INDEX); assertEquals("index changes", 1, AUTO_CHANGES.size()); assertEquals("change type", ChangeType.ADD, AUTO_CHANGES.get(0).getChangeType()); } @Test public void dropIndexUnspecified() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("x", "x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); validate(t1, t2, NO_CHANGES, AUTO_CHANGES, ChangeLevel.INDEX); assertEquals("index changes", 1, AUTO_CHANGES.size()); } @Test(expected=DropIndexNotPresentException.class) public void dropIndexUnknown() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); validate(t1, t2, NO_CHANGES, asList(TableChange.createDrop("x")), null); } @Test public void modifyIndexNotChanged() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("x", "x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("x", "x").pk("id")); TableChangeValidator tcv = new TableChangeValidator(t1, t2, NO_CHANGES, asList( TableChange.createModify("x", "x"))); tcv.compare(); assertEquals("Final change level", ChangeLevel.NONE, tcv.getFinalChangeLevel()); assertEquals("Unmodified change count", 1, tcv.getUnmodifiedChanges().size()); } @Test(expected=ModifyIndexNotPresentException.class) public void modifyIndexUnknown() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); validate(t1, t2, NO_CHANGES, asList(TableChange.createModify("y", "y")), null); } @Test public void modifyIndexUnspecified() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").colBigInt("y").key("k", "x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").colBigInt("y").key("k", "y").pk("id")); validate(t1, t2, NO_CHANGES, AUTO_CHANGES, ChangeLevel.INDEX); assertEquals("index changes", 1, AUTO_CHANGES.size()); } @Test public void modifyIndexedColumnIndexUnspecified() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").key("x", "x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colString("x", 32).key("x", "x").pk("id")); validate(t1, t2, asList(TableChange.createModify("x", "x")), AUTO_CHANGES, ChangeLevel.TABLE); assertEquals("index changes", 1, AUTO_CHANGES.size()); } // // Group // @Test public void modifyPKColumnTypeSingleTableGroup() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(TABLE_NAME).colString("id", 32).pk("id")); validate(t1, t2, asList(TableChange.createModify("id", "id")), asList(TableChange.createModify(Index.PRIMARY, Index.PRIMARY)), ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE)), false, true, NO_INDEX_CHANGE, NO_IDENTITY_CHANGE); } @Test public void dropPrimaryKeySingleTableGroup() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id")); validate(t1, t2, asList(TableChange.createAdd(Column.ROW_ID_NAME)), asList(TableChange.createModify(Index.PRIMARY, Index.PRIMARY)), ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE)), false, true, NO_INDEX_CHANGE, "+[" + Column.ROW_ID_NAME + "]"); } @Test public void dropPrimaryKeyColumn() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").pk("id").colString("name", 32)); Table t2 = table(builder(TABLE_NAME).colString("name", 32)); validate(t1, t2, asList(TableChange.createDrop("id"),TableChange.createAdd(Column.ROW_ID_NAME)), asList(TableChange.createModify(Index.PRIMARY, Index.PRIMARY)), ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE)), false, true, NO_INDEX_CHANGE, "+[" + Column.ROW_ID_NAME + "]" ); } @Test public void dropPrimaryKeyIdentityColumn() { Table t1 = table(builder(TABLE_NAME).autoIncInt("id",1).pk("id").colString("name", 32)); Table t2 = table(builder(TABLE_NAME).colString("name", 32)); validate(t1, t2, asList(TableChange.createDrop("id"),TableChange.createAdd(Column.ROW_ID_NAME)), AUTO_CHANGES, ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE)), false, true, NO_INDEX_CHANGE, "-[test.temp-seq-t-id]+[" + Column.ROW_ID_NAME + "]" ); } @Test public void dropParentJoinTwoTableGroup() { TableName parentName = new TableName(SCHEMA, "parent"); Table t1 = table( builder(parentName).colInt("id").pk("id"). table(TABLE_NAME).colBigInt("id").colInt("pid").pk("id").joinTo(SCHEMA, "parent", "fk").on("pid", "id"), TABLE_NAME ); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colInt("pid").pk("id")); validate(t1, t2, NO_CHANGES, asList(TableChange.createDrop("fk")), ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, true, ParentChange.DROP)), true, false, NO_INDEX_CHANGE, NO_IDENTITY_CHANGE); } @Test public void dropPrimaryKeyMiddleOfGroup() { TableName cName = new TableName(SCHEMA, "c"); TableName oName = new TableName(SCHEMA, "o"); TableName iName = new TableName(SCHEMA, "i"); NewAISBuilder builder1 = AISBBasedBuilder.create(typesTranslator); builder1.table(cName).colBigInt("id", false).pk("id") .table(oName).colBigInt("id", false).colBigInt("cid", true).pk("id").joinTo(SCHEMA, "c", "fk1").on("cid", "id") .table(iName).colBigInt("id", false).colBigInt("oid", true).pk("id").joinTo(SCHEMA, "o", "fk2").on("oid", "id"); NewAISBuilder builder2 = AISBBasedBuilder.create(typesTranslator); builder2.table(cName).colBigInt("id", false).pk("id") .table(oName).colBigInt("id", false).colBigInt("cid", true).joinTo(SCHEMA, "c", "fk1").on("cid", "id") .table(iName).colBigInt("id", false).colBigInt("oid", true).pk("id").joinTo(SCHEMA, "o", "fk2").on("oid", "id"); Table t1 = builder1.unvalidatedAIS().getTable(oName); Table t2 = builder2.unvalidatedAIS().getTable(oName); validate( t1, t2, asList(TableChange.createAdd(Column.ROW_ID_NAME)), AUTO_CHANGES, ChangeLevel.GROUP, asList( changeDesc(oName, oName, false, ParentChange.NONE), changeDesc(iName, iName, true, ParentChange.DROP) ), false, true, NO_INDEX_CHANGE, "+[" + Column.ROW_ID_NAME+"]" ); } // // Multi-part // @Test public void addAndDropColumn() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("x").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colBigInt("y").pk("id")); validate(t1, t2, asList(TableChange.createDrop("x"), TableChange.createAdd("y")), NO_CHANGES, ChangeLevel.TABLE); } @Test public void addAndDropMultipleColumnAndIndex() { Table t1 = table(builder(TABLE_NAME).colBigInt("id").colDouble("d").colInt("l").colString("s", 32). key("d", "d").key("l", "l").uniqueKey("k", "l", "d").pk("id")); Table t2 = table(builder(TABLE_NAME).colBigInt("id").colDouble("d").colVarBinary("v", 32).colString("s", 64). key("d", "d").key("v", "v").uniqueKey("k", "v", "d").pk("id")); validate( t1, t2, asList(TableChange.createDrop("l"), TableChange.createModify("s", "s"), TableChange.createAdd("v")), asList(TableChange.createDrop("l"), TableChange.createAdd("v"), TableChange.createModify("k", "k")), ChangeLevel.TABLE, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE, "PRIMARY", "PRIMARY", "d", "d")) ); } @Test public void addColumnAndIndex() { Table t1 = table (builder(TABLE_NAME).colInt("c1", true).colInt("c2", true).colInt("c3", true)); Table t2 = table (builder(TABLE_NAME).colInt("c1", true).colInt("c2", true).colInt("c3", true).colInt("c4").key("c4", "c4")); validate ( t1, t2, asList(TableChange.createAdd("c4")), asList(TableChange.createAdd("c4")), ChangeLevel.TABLE, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE, "PRIMARY", "PRIMARY")) ); } @Test public void addPKtoNoPkTable() { final TableName SEQ_NAME = new TableName(SCHEMA, "seq-1"); Table t1 = table (builder(TABLE_NAME).colInt("c1",true)); Table t2 = table (builder(TABLE_NAME).colInt("c1", true).colInt("id").pk("id").sequence(SEQ_NAME.getTableName())); t2.getColumn("id").setIdentityGenerator(t2.getAIS().getSequence(SEQ_NAME)); t2.getColumn("id").setDefaultIdentity(true); validate (t1, t2, asList(TableChange.createAdd("id"), TableChange.createDrop(Column.ROW_ID_NAME)), AUTO_CHANGES, ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE)), false, true, NO_INDEX_CHANGE, "-[test.t___row_id_seq]+[id]" ); } @Test public void dropIdentityPK() { final TableName SEQ_NAME = new TableName(SCHEMA, "seq-1"); Table t1 = table (builder(TABLE_NAME).colInt("c1", true).colInt("id").pk("id").sequence(SEQ_NAME.getTableName())); t1.getColumn("id").setIdentityGenerator(t1.getAIS().getSequence(SEQ_NAME)); t1.getColumn("id").setDefaultIdentity(true); Table t2 = table (builder(TABLE_NAME).colInt("c1",true)); validate (t1, t2, asList(TableChange.createDrop("id"), TableChange.createAdd(Column.ROW_ID_NAME)), AUTO_CHANGES, ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE)), false, true, NO_INDEX_CHANGE, "-[test.seq-1]+[" + Column.ROW_ID_NAME + "]" ); } // // Auto index changes // @Test public void addDropAndModifyIndexAutoChanges() { Table t1 = table(builder(TABLE_NAME).colInt("c1").colInt("c2").colInt("c3").key("c1", "c1").key("c3", "c3")); Table t2 = table(builder(TABLE_NAME).colInt("c1").colInt("c2").colString("c3", 32).key("c2", "c2").key("c3", "c3")); validate( t1, t2, asList(TableChange.createModify("c3", "c3")), AUTO_CHANGES, ChangeLevel.TABLE, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE, "PRIMARY", "PRIMARY")), false, false, NO_INDEX_CHANGE, NO_IDENTITY_CHANGE ); assertEquals("index changes", 3, AUTO_CHANGES.size()); } // // Group Index changes // @Test public void dropColumnInGroupIndex() { NewAISBuilder builder = AISBBasedBuilder.create(SCHEMA, typesTranslator); builder.table("p").colInt("id").colInt("x").pk("id") .table(TABLE).colInt("id").colInt("pid").colInt("y").pk("id").joinTo(SCHEMA, "p", "fk").on("pid", "id") .groupIndex("x_y", Index.JoinType.LEFT).on(TABLE, "y").and("p", "x"); Table t1 = builder.unvalidatedAIS().getTable(TABLE_NAME); builder = AISBBasedBuilder.create(SCHEMA, typesTranslator); builder.table("p").colInt("id").colInt("x").pk("id") .table(TABLE).colInt("id").colInt("pid").pk("id").joinTo(SCHEMA, "p", "fk").on("pid", "id"); Table t2 = builder.unvalidatedAIS().getTable(TABLE_NAME); final String KEY1 = Index.PRIMARY; final String KEY2 = "fk"; validate( t1, t2, asList(TableChange.createDrop("y")), NO_CHANGES, ChangeLevel.TABLE, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE, KEY1, KEY1, KEY2, KEY2)), false, false, new String[]{ "[x_y]", EMPTY_MAP_STR, EMPTY_MAP_STR }, NO_IDENTITY_CHANGE ); } @Test public void dropGFKFrommMiddleWithGroupIndexes() { TableName iName = new TableName(SCHEMA, "i"); NewAISBuilder builder = AISBBasedBuilder.create(SCHEMA, typesTranslator); builder.table("p").colInt("id").colInt("x").pk("id") .table(TABLE).colInt("id").colInt("pid").colInt("y").pk("id").joinTo(SCHEMA, "p", "fk1").on("pid", "id") .table(iName).colInt("id").colInt("tid").colInt("z").pk("id").joinTo(SCHEMA, TABLE, "fk2").on("tid", "id") .groupIndex("x_y", Index.JoinType.LEFT).on(TABLE, "y").and("p", "x") // spans 2 .groupIndex("x_y_z", Index.JoinType.LEFT).on("i", "z").and(TABLE, "y").and("p", "x"); // spans 3 Table t1 = builder.unvalidatedAIS().getTable(TABLE_NAME); builder = AISBBasedBuilder.create(SCHEMA, typesTranslator); builder.table("p").colInt("id").colInt("x").pk("id") .table(TABLE).colInt("id").colInt("pid").colInt("y").pk("id").key("fk1", "pid") .table(iName).colInt("id").colInt("tid").colInt("z").pk("id").joinTo(SCHEMA, TABLE, "fk2").on("tid", "id"); Table t2 = builder.unvalidatedAIS().getTable(TABLE_NAME); validate( t1, t2, NO_CHANGES, NO_CHANGES, ChangeLevel.GROUP, asList(changeDesc(TABLE_NAME, TABLE_NAME, true, ParentChange.DROP), changeDesc(iName, iName, true, ParentChange.UPDATE)), true, false, new String[]{ "[x_y, x_y_z]", EMPTY_MAP_STR, EMPTY_MAP_STR }, NO_IDENTITY_CHANGE ); } // // FK change // private void fkTest(boolean isAdd) { TableName PARENT = new TableName(SCHEMA, "p"); Table t1 = table(builder(PARENT).colBigInt("pid").pk("pid").table(TABLE).colBigInt("cid").colBigInt("pid").pk("cid"), TABLE_NAME); NewAISBuilder builder = builder(PARENT).colBigInt("pid").pk("pid").table(TABLE).colBigInt("cid").colBigInt("pid").pk("cid"); AkibanInformationSchema ais = builder.unvalidatedAIS(); Table t2 = ais.getTable(TABLE_NAME); Table p2 = ais.getTable(PARENT); ForeignKey.create(ais, "__fk_1", t2, asList(t2.getColumn("pid")), p2, asList(p2.getColumn("pid")), Action.RESTRICT, Action.RESTRICT, false, false); Table pre = isAdd ? t1 : t2; Table post = isAdd ? t2 : t1; validate(pre, post, NO_CHANGES, NO_CHANGES, ChangeLevel.METADATA_CONSTRAINT, asList(changeDesc(TABLE_NAME, TABLE_NAME, false, ParentChange.NONE, "PRIMARY", "PRIMARY"), changeDesc(PARENT, PARENT, false, ParentChange.NONE, "PRIMARY", "PRIMARY")), false, false, NO_INDEX_CHANGE, NO_IDENTITY_CHANGE); } @Test public void addFK() { fkTest(true); } @Test public void dropFK() { fkTest(false); } }