/**
* 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.Column;
import com.foundationdb.ais.model.Index;
import com.foundationdb.ais.model.Sequence;
import com.foundationdb.ais.model.Table;
import com.foundationdb.ais.model.TableName;
import com.foundationdb.ais.model.TestAISBuilder;
import com.foundationdb.ais.model.aisb2.AISBBasedBuilder;
import com.foundationdb.ais.model.aisb2.NewAISBuilder;
import com.foundationdb.ais.util.TableChange;
import com.foundationdb.ais.util.TableChangeValidatorException.UndeclaredColumnChangeException;
import com.foundationdb.qp.expression.IndexKeyRange;
import com.foundationdb.qp.operator.API;
import com.foundationdb.qp.operator.QueryBindings;
import com.foundationdb.qp.operator.QueryContext;
import com.foundationdb.qp.operator.SimpleQueryContext;
import com.foundationdb.qp.operator.StoreAdapter;
import com.foundationdb.qp.row.Row;
import com.foundationdb.qp.rowtype.IndexRowType;
import com.foundationdb.qp.rowtype.RowType;
import com.foundationdb.qp.rowtype.Schema;
import com.foundationdb.qp.rowtype.TableRowType;
import com.foundationdb.qp.util.SchemaCache;
import com.foundationdb.server.error.NoColumnsInTableException;
import com.foundationdb.server.error.NotNullViolationException;
import com.foundationdb.server.error.UnsupportedSQLException;
import com.foundationdb.sql.StandardException;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static com.foundationdb.ais.util.TableChangeValidator.ChangeLevel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class AlterTableBasicIT extends AlterTableITBase {
private static final Logger LOG = LoggerFactory.getLogger(AlterTableBasicIT.class.getName());
private int cid;
private int oid;
private int iid;
private void createAndLoadSingleTableGroup() {
cid = createTable(SCHEMA, "c", "id int not null primary key, c1 char(5)");
writeRows(
row(cid, 1, "10"),
row(cid, 2, "20"),
row(cid, 3, "30")
);
}
private void createAndLoadCOI() {
createAndLoadCOI(SCHEMA);
}
private void createAndLoadCOI(String schema) {
cid = createTable(schema, "c", "id int not null primary key, c1 char(1)");
oid = createTable(schema, "o", "id int not null primary key, cid int, o1 int, grouping foreign key(cid) references c(id)");
iid = createTable(schema, "i", "id int not null primary key, oid int, i1 int, grouping foreign key(oid) references o(id)");
writeRows(
row(cid, 1L, "a"),
row(oid, 10, 1, 11),
row(iid, 100, 10, 110),
row(iid, 101, 10, 111),
row(oid, 11, 1, 12),
row(iid, 111, 11, 122),
row(cid, 2L, "b"),
// no 3L
row(oid, 30, 3, 33),
row(iid, 300, 30, 330)
);
}
private IndexRowType indexRowType(Index index) {
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
return schema.indexRowType(index);
}
private void scanAndCheckIndex(IndexRowType type, Row... expectedRows) {
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
StoreAdapter adapter = newStoreAdapter(schema);
QueryContext queryContext = new SimpleQueryContext(adapter);
QueryBindings queryBindings = queryContext.createBindings();
compareRows(
expectedRows,
API.cursor(
API.indexScan_Default(type, false, IndexKeyRange.unbounded(type)),
queryContext, queryBindings
)
);
}
@Test(expected=UndeclaredColumnChangeException.class)
public void unspecifiedColumnChange() {
NewAISBuilder builder = AISBBasedBuilder.create(ddl().getTypesTranslator());
builder.table(SCHEMA, "c").colInt("c1").pk("c1");
Table table = builder.ais().getTable(SCHEMA, "c");
ddl().createTable(session(), table);
builder = AISBBasedBuilder.create(ddl().getTypesTranslator());
builder.table(SCHEMA, "c").colInt("c1").colInt("c2").colInt("c3").pk("c1");
table = builder.ais().getTable(SCHEMA, "c");
ddl().alterTable(session(), table.getName(), table,
Arrays.asList(TableChange.createAdd("c2")), NO_CHANGES,
null);
}
@Test
public void dropSingleColumnFromMultiColumnPK() throws StandardException {
cid = createTable(SCHEMA, "c", "c1 int not null, c2 char(1), c3 int not null, primary key(c1,c3)");
writeRows(
row(cid, 1, "A", 50L),
row(cid, 2, "B", 20L),
row(cid, 5, "C", 10L)
);
runAlter(ChangeLevel.GROUP, "ALTER TABLE c DROP COLUMN c1");
expectRows(
cid,
row(cid, "C", 10),
row(cid, "B", 20),
row(cid, "A", 50)
);
Index index = getTable(SCHEMA, "c").getIndex(Index.PRIMARY);
expectRows(
index,
row(index, 10),
row(index, 20),
row(index, 50)
);
}
@Test
public void dropPrimaryKeyMiddleOfGroup() throws StandardException {
createAndLoadCOI();
// Will yield 2 groups: C-O and I
runAlter(ChangeLevel.GROUP, "ALTER TABLE o DROP PRIMARY KEY");
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
RowType cType = schema.tableRowType(getTable(SCHEMA, "c"));
RowType oType = schema.tableRowType(getTable(SCHEMA, "o"));
RowType iType = schema.tableRowType(getTable(SCHEMA, "i"));
StoreAdapter adapter = newStoreAdapter(schema);
int pk = 1;
compareRows(
new Row[]{
testRow(cType, 1, "a"),
testRow(oType, 10, 1, 11, pk++),
testRow(oType, 11, 1, 12, pk++),
testRow(cType, 2, "b"),
testRow(oType, 30, 3, 33, pk++),
},
adapter.newGroupCursor(cType.table().getGroup())
);
compareRows(
new Row[]{
testRow(iType, 100, 10, 110),
testRow(iType, 101, 10, 111),
testRow(iType, 111, 11, 122),
testRow(iType, 300, 30, 330)
},
adapter.newGroupCursor(iType.table().getGroup())
);
}
@Test
public void cannotAddNotNullWithNoDefault() throws StandardException {
createAndLoadSingleTableGroup();
try {
runAlter("ALTER TABLE c ADD COLUMN c2 INT NOT NULL DEFAULT NULL");
fail("Expected NotNullViolationException");
} catch(NotNullViolationException e) {
// Expected
}
// Check that schema change was rolled back correctly
expectRows(
cid,
row(cid, 1, "10"),
row(cid, 2, "20"),
row(cid, 3, "30")
);
}
@Test
public void addNotNullColumnDefault() throws StandardException {
createAndLoadSingleTableGroup();
runAlter("ALTER TABLE c ADD COLUMN c2 INT NOT NULL DEFAULT 0");
expectRows(
cid,
row(cid, 1, "10", 0),
row(cid, 2, "20", 0),
row(cid, 3, "30", 0)
);
}
@Test
public void addSingleColumnSingleTableGroup() throws StandardException {
createAndLoadSingleTableGroup();
runAlter("ALTER TABLE c ADD COLUMN c2 INT NULL");
expectRows(
cid,
row(cid, 1, "10", null),
row(cid, 2, "20", null),
row(cid, 3, "30", null)
);
}
@Test
public void addColumnIndexSingleTableNoPrimaryKey() throws StandardException {
TableName cName = tableName(SCHEMA, "c");
NewAISBuilder builder = AISBBasedBuilder.create(ddl().getTypesTranslator());
builder.table(cName).colInt("c1", true).colInt("c2", true).colInt("c3", true);
ddl().createTable(session(), builder.unvalidatedAIS().getTable(cName));
// Note: Not using standard id due to null index contents
int tableId = tableId(cName);
writeRows(
row(tableId, 1, 2, 3),
row(tableId, 4, 5, 6),
row(tableId, 7, 8, 9)
);
builder = AISBBasedBuilder.create(ddl().getTypesTranslator());
builder.table(cName).colInt("c1", true).colInt("c2", true).colInt("c3", true).colInt("c4", true).key("c4", "c4");
List<TableChange> changes = new ArrayList<>();
changes.add(TableChange.createAdd("c4"));
ddl().alterTable(session(), cName, builder.unvalidatedAIS().getTable(cName), changes, changes, queryContext());
expectRowsSkipInternal(
tableId,
row(tableId, 1, 2, 3, null),
row(tableId, 4, 5, 6, null),
row(tableId, 7, 8, 9, null)
);
Index index =getTable(tableId).getIndex("c4");
expectRows(
index,
row(index, null, 1),
row(index, null, 2),
row(index, null, 3)
);
ddl().dropTable(session(), cName);
}
@Test
public void addSingleColumnRootOfGroup() throws StandardException {
createAndLoadCOI();
runAlter("ALTER TABLE c ADD COLUMN c2 INT NULL");
expectRows(
cid,
row(cid, 1, "a", null),
row(cid, 2, "b", null)
);
}
@Test
public void addSingleColumnMiddleOfGroup() throws StandardException {
createAndLoadCOI();
runAlter("ALTER TABLE o ADD COLUMN o2 INT NULL");
expectRows(
oid,
row(oid, 10, 1, 11, null),
row(oid, 11, 1, 12, null),
row(oid, 30, 3, 33, null)
);
}
@Test
public void addSingleColumnLeafOfGroup() throws StandardException {
createAndLoadCOI();
runAlter("ALTER TABLE i ADD COLUMN i2 INT NULL");
expectRows(
iid,
row(iid, 100, 10, 110, null),
row(iid, 101, 10, 111, null),
row(iid, 111, 11, 122, null),
row(iid, 300, 30, 330, null)
);
}
@Test
public void dropSingleColumnSingleTableGroup() throws StandardException {
createAndLoadSingleTableGroup();
runAlter("ALTER TABLE c DROP COLUMN c1");
expectRows(
cid,
row(cid, 1),
row(cid, 2),
row(cid, 3)
);
}
@Test
public void dropSingleColumnRootOfGroup() throws StandardException {
createAndLoadCOI();
runAlter("ALTER TABLE c DROP COLUMN c1");
expectRows(
cid,
row(cid, 1),
row(cid, 2)
);
}
@Test
public void dropSingleColumnMiddleOfGroup() throws StandardException {
createAndLoadCOI();
runAlter("ALTER TABLE o DROP COLUMN o1");
expectRows(
oid,
row(oid, 10, 1),
row(oid, 11, 1),
row(oid, 30, 3)
);
}
@Test
public void dropSingleColumnLeafOfGroup() throws StandardException {
createAndLoadCOI();
runAlter("ALTER TABLE i DROP COLUMN i1");
expectRows(
iid,
row(iid, 100, 10),
row(iid, 101, 10),
row(iid, 111, 11),
row(iid, 300, 30)
);
}
@Test
public void dropSingleColumnOfSingleColumnIndex() throws StandardException {
createAndLoadSingleTableGroup();
createIndex(SCHEMA, "c", "c1", "c1");
runAlter("ALTER TABLE c DROP COLUMN c1");
expectIndexes(cid, "PRIMARY");
expectRows(
cid,
row(cid, 1),
row(cid, 2),
row(cid, 3)
);
}
@Test
public void dropSingleColumnOfMultiColumnIndex() throws StandardException {
cid = createTable(SCHEMA, "c", "id int not null primary key, c1 int, c2 int");
createIndex(SCHEMA, "c", "c1_c2", "c1", "c2");
writeRows(
row(cid, 1, 11, 12),
row(cid, 2, 21, 22),
row(cid, 3, 31, 32),
row(cid, 10, 101, 102)
);
runAlter("ALTER TABLE c DROP COLUMN c1");
Index index = getTable(SCHEMA, "c").getIndex("c1_c2");
expectRows(
index,
row(index, 12, 1),
row(index, 22, 2),
row(index, 32, 3),
row(index, 102, 10)
);
}
@Test
public void dropSingleColumnOfMultiColumnGroupIndex() throws StandardException {
createAndLoadCOI();
createLeftGroupIndex(C_NAME, "c1_o1_o2", "c.c1", "o.o1", "i.i1");
runAlter("ALTER TABLE o DROP COLUMN o1");
AkibanInformationSchema ais = ddl().getAIS(session());
Index index = ais.getGroup(C_NAME).getIndex("c1_o1_o2");
assertNotNull("Index still exists", index);
assertEquals("Index column count", 2, index.getKeyColumns().size());
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
IndexRowType indexRowType = schema.indexRowType(index);
StoreAdapter adapter = newStoreAdapter(schema);
QueryContext queryContext = new SimpleQueryContext(adapter);
QueryBindings queryBindings = queryContext.createBindings();
compareRows(
new Row[] {
testRow(indexRowType, "a", 110, 1, 10, 100),
testRow(indexRowType, "a", 111, 1, 10, 101),
testRow(indexRowType, "a", 122, 1, 11, 111),
},
API.cursor(
API.indexScan_Default(indexRowType, false, IndexKeyRange.unbounded(indexRowType)),
queryContext, queryBindings
)
);
}
@Test
public void dropGroupingForeignKeyTableInGroupIndex() throws StandardException {
createAndLoadCOI();
createLeftGroupIndex(new TableName(SCHEMA, "c"), "c1_o1_i1", "c.c1", "o.o1", "i.i1");
runAlter(ChangeLevel.GROUP, "ALTER TABLE o DROP GROUPING FOREIGN KEY");
AkibanInformationSchema ais = ddl().getAIS(session());
Index index = ais.getGroup(C_NAME).getIndex("c1_o1_i1");
assertNull("Index should not exist on c group", index);
index = ais.getGroup(O_NAME).getIndex("c1_o1_i1");
assertNull("Index should not exist on o group", index);
}
@Test
public void changeDataTypeSingleColumnSingleTableGroup() throws StandardException {
createAndLoadSingleTableGroup();
runAlter("ALTER TABLE c ALTER COLUMN c1 SET DATA TYPE int");
expectRows(
cid,
row(cid, 1, 10),
row(cid, 2, 20),
row(cid, 3, 30)
);
}
@Test
public void changeDataTypeSingleColumnInIndex() throws StandardException {
createAndLoadSingleTableGroup();
createIndex(SCHEMA, "c", "c1", "c1");
runAlter("ALTER TABLE c ALTER COLUMN c1 SET DATA TYPE int");
expectRows(
cid,
row(cid, 1, 10),
row(cid, 2, 20),
row(cid, 3, 30)
);
Index index = getTable(SCHEMA, "c").getIndex("c1");
expectRows(
index,
row(index, 10, 1),
row(index, 20, 2),
row(index, 30, 3)
);
}
@Test
public void addDropAndAlterColumnSingleTableGroup() throws StandardException {
cid = createTable(SCHEMA, "c", "c1 int not null primary key, c2 char(5), c3 int, c4 char(1)");
writeRows(
row(cid, 1, "one", 10, "A"),
row(cid, 2, "two", 20, "B"),
row(cid, 3, "three", 30, "C"),
row(cid, 10, "ten", 100, "D")
);
// Our parser doesn't (yet) support multi-action alters, manually build parameters
// ALTER TABLE c ADD COLUMN c5 INT, DROP COLUMN c2, ALTER COLUMN c3 SET DATA TYPE char(3)
TestAISBuilder builder = new TestAISBuilder(typesRegistry());
builder.table(SCHEMA, "c");
builder.column(SCHEMA, "c", "c1", 0, "MCOMPAT", "int", false);
builder.column(SCHEMA, "c", "c3", 1, "MCOMPAT", "char", 3L, null, true);
builder.column(SCHEMA, "c", "c4", 2, "MCOMPAT", "char", 1L, null, true);
builder.column(SCHEMA, "c", "c5", 3, "MCOMPAT", "int", true);
builder.pk(SCHEMA, "c");
builder.indexColumn(SCHEMA, "c", Index.PRIMARY, "c1", 0, true, null);
builder.basicSchemaIsComplete();
builder.createGroup("c", SCHEMA);
builder.addTableToGroup(C_NAME, SCHEMA, "c");
builder.groupingIsComplete();
Table newTable = builder.akibanInformationSchema().getTable(SCHEMA, "c");
List<TableChange> changes = new ArrayList<>();
changes.add(TableChange.createAdd("c5"));
changes.add(TableChange.createDrop("c2"));
changes.add(TableChange.createModify("c3", "c3"));
ddl().alterTable(session(), new TableName(SCHEMA, "c"), newTable, changes, NO_CHANGES, queryContext());
expectRows(
cid,
row(cid, 1, "10", "A", null),
row(cid, 2, "20", "B", null),
row(cid, 3, "30", "C", null),
row(cid, 10, "100", "D", null)
);
}
@Test
public void addUniqueKeyExistingColumn() throws StandardException {
createAndLoadSingleTableGroup();
runAlter(ChangeLevel.INDEX, "ALTER TABLE c ADD UNIQUE(c1)");
expectIndexes(cid, "PRIMARY", "c1");
Index index = getTable(SCHEMA, "c").getIndex("c1");
expectRows(
index,
row(index, "10", 1),
row(index, "20", 2),
row(index, "30", 3)
);
}
@Test
public void alterColumnNotNullToNull() throws StandardException {
cid = createTable(SCHEMA, "c", "id int not null primary key, c1 char(5) not null");
writeRows(
row(cid, 1, "10"),
row(cid, 2, "20"),
row(cid, 3, "30")
);
runAlter(ChangeLevel.METADATA, "ALTER TABLE c ALTER COLUMN c1 NULL");
// Just check metadata
// Insert needs more plumbing (e.g. Insert_Default), checked in test-alter-nullability.yaml
Table table = getTable(SCHEMA, "c");
assertEquals("c1 nullable", true, table.getColumn("c1").getNullable());
}
@Test
public void alterColumnNullToNotNull() throws StandardException {
cid = createTable(SCHEMA, "c", "id int not null primary key, c1 char(5) null");
writeRows(
row(cid, 1, "10"),
row(cid, 2, "20"),
row(cid, 3, "30")
);
runAlter(ChangeLevel.METADATA_CONSTRAINT, "ALTER TABLE c ALTER COLUMN c1 NOT NULL");
// Just check metadata
// Insert needs more plumbing (e.g. Insert_Default), checked in test-alter-nullability.yaml
Table table = getTable(SCHEMA, "c");
assertEquals("c1 nullable", false, table.getColumn("c1").getNullable());
}
@Test
public void dropUniqueAddIndexSameName() {
cid = createTable(SCHEMA, "c", "id int not null primary key, c1 char(1), c2 char(1), constraint foo unique(c1)");
writeRows(
row(cid, 1, "A", "3"),
row(cid, 2, "B", "2"),
row(cid, 3, "C", "1")
);
AkibanInformationSchema ais = aisCloner().clone(ddl().getAIS(session()));
Table table = ais.getTable(SCHEMA, "c");
table.removeIndexes(Collections.singleton(table.getIndex("foo")));
AISBuilder builder = new AISBuilder(ais);
builder.index(SCHEMA, "c", "foo");
builder.indexColumn(SCHEMA, "c", "foo", "c2", 0, true, null);
List<TableChange> changes = new ArrayList<>();
changes.add(TableChange.createDrop("foo"));
changes.add(TableChange.createAdd("foo"));
ddl().alterTable(session(), new TableName(SCHEMA, "c"), table, NO_CHANGES, changes, queryContext());
expectIndexes(cid, "foo", "PRIMARY");
Index index = getTable(SCHEMA, "c").getIndex("foo");
expectRows(
index,
row(index, "1", 3),
row(index, "2", 2),
row(index, "3", 1)
);
}
@Test
public void modifyIndex() {
cid = createTable(SCHEMA, "c", "id int not null primary key, c1 char(1), c2 char(1)");
createIndex(SCHEMA, "c", "foo", "c1");
writeRows(
row(cid, 1L, "A", "3"),
row(cid, 2L, "B", "2"),
row(cid, 3L, "C", "1")
);
AkibanInformationSchema ais = aisCloner().clone(ddl().getAIS(session()));
Table table = ais.getTable(SCHEMA, "c");
table.removeIndexes(Collections.singleton(table.getIndex("foo")));
AISBuilder builder = new AISBuilder(ais);
builder.index(SCHEMA, "c", "foo");
builder.indexColumn(SCHEMA, "c", "foo", "c2", 0, true, null);
builder.indexColumn(SCHEMA, "c", "foo", "c1", 1, true, null);
List<TableChange> changes = new ArrayList<>();
changes.add(TableChange.createModify("foo", "foo"));
ddl().alterTable(session(), new TableName(SCHEMA, "c"), table, NO_CHANGES, changes, queryContext());
expectIndexes(cid, "foo", "PRIMARY");
Index index = getTable(SCHEMA, "c").getIndex("foo");
expectRows(
index,
row(index, "1", "C", 3L),
row(index, "2", "B", 2L),
row(index, "3", "A", 1L)
);
}
@Test
public void addColumnDropColumnAddIndexOldNewMiddleOfGroup() throws StandardException {
createAndLoadCOI();
// ALTER TABLE o DROP COLUMN o1, ADD COLUMN o1 INT, ADD INDEX x(o1), ADD INDEX y(cid)
AkibanInformationSchema ais = aisCloner().clone(ddl().getAIS(session()));
Table table = ais.getTable(SCHEMA, "o");
table.dropColumn("o1");
TestAISBuilder builder = new TestAISBuilder(ais, typesRegistry());
builder.column(SCHEMA, "o", "o1", 2, "MCOMPAT", "int", true);
builder.index(SCHEMA, "o", "x");
builder.indexColumn(SCHEMA, "o", "x", "o1", 0, true, null);
builder.index(SCHEMA, "o", "y");
builder.indexColumn(SCHEMA, "o", "y", "cid", 0, true, null);
List<TableChange> columnChanges = new ArrayList<>();
columnChanges.add(TableChange.createDrop("o1"));
columnChanges.add(TableChange.createAdd("o1"));
List<TableChange> indexChanges = new ArrayList<>();
indexChanges.add(TableChange.createAdd("x"));
indexChanges.add(TableChange.createAdd("y"));
ddl().alterTable(session(), new TableName(SCHEMA, "o"), table, columnChanges, indexChanges, queryContext());
expectRows(
oid,
row(oid, 10, 1, null),
row(oid, 11, 1, null),
row(oid, 30, 3, null)
);
expectIndexes(oid, "PRIMARY", "x", "y");
Index index = getTable(SCHEMA, "o").getIndex("x");
expectRows(
index,
row(index, null, 1, 10),
row(index, null, 1, 11),
row(index, null, 3, 30)
);
index = getTable(SCHEMA, "o").getIndex("y");
expectRows(
index,
row(index, 1, 10),
row(index, 1, 11),
row(index, 3, 30)
);
}
@Test
public void addGroupingForeignSingleToTwoTableGroup() throws StandardException {
cid = createTable(SCHEMA, "c", "id int not null primary key, v varchar(32)");
oid = createTable(SCHEMA, "o", "id int not null primary key, cid int, tag char(1), grouping foreign key(cid) references c(id)");
iid = createTable(SCHEMA, "i", "id int not null primary key, spare_id int, tag2 char(1)");
writeRows(
row(cid, 1, "asdf"),
row(cid, 5, "qwer"),
row(cid, 10, "zxcv")
);
writeRows(
row(oid, 10, 1, "a"),
row(oid, 11, 1, "b"),
row(oid, 60, 6, "c")
);
writeRows(
row(iid, 100, 10, "d"),
row(iid, 101, 10, "e"),
row(iid, 102, 10, "f"),
row(iid, 200, 20, "d")
);
runAlter(ChangeLevel.GROUP, "ALTER TABLE i ADD GROUPING FOREIGN KEY(spare_id) REFERENCES o(id)");
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
RowType cType = schema.tableRowType(getTable(SCHEMA, "c"));
RowType oType = schema.tableRowType(getTable(SCHEMA, "o"));
RowType iType = schema.tableRowType(getTable(SCHEMA, "i"));
StoreAdapter adapter = newStoreAdapter(schema);
compareRows(
new Row[] {
// null c
// no o20
testRow(iType, 200, 20, "d"),
testRow(cType, 1L, "asdf"),
testRow(oType, 10, 1, "a"),
testRow(iType, 100, 10, "d"),
testRow(iType, 101, 10, "e"),
testRow(iType, 102, 10, "f"),
testRow(oType, 11, 1, "b"),
testRow(cType, 5, "qwer"),
// no c6
testRow(oType, 60, 6, "c"),
testRow(cType, 10, "zxcv")
},
adapter.newGroupCursor(cType.table().getGroup())
);
}
@Test
public void simpleDropGroupingForeignKey() throws StandardException {
cid = createTable(SCHEMA, "c", "id int not null primary key, v varchar(32)");
oid = createTable(SCHEMA, "o", "id int not null primary key, cid int, tag char(1), grouping foreign key(cid) references c(id)");
writeRows(
row(cid, 1, "asdf"),
row(cid, 5, "qwer"),
row(cid, 10, "zxcv")
);
writeRows(
row(oid, 10, 1, "a"),
row(oid, 11, 1, "b"),
row(oid, 60, 6, "c")
);
runAlter(ChangeLevel.GROUP, "ALTER TABLE o DROP GROUPING FOREIGN KEY");
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
RowType cType = schema.tableRowType(getTable(SCHEMA, "c"));
RowType oType = schema.tableRowType(getTable(SCHEMA, "o"));
StoreAdapter adapter = newStoreAdapter(schema);
compareRows(
new Row[] {
testRow(cType, 1L, "asdf"),
testRow(cType, 5, "qwer"),
testRow(cType, 10, "zxcv")
},
adapter.newGroupCursor(cType.table().getGroup())
);
compareRows(
new Row[] {
testRow(oType, 10, 1, "a"),
testRow(oType, 11, 1, "b"),
testRow(oType, 60, 6, "c"),
},
adapter.newGroupCursor(oType.table().getGroup())
);
}
@Test
public void addGroupingForeignKeyToExistingParent() throws StandardException {
cid = createTable(SCHEMA, "c", "id int not null primary key, v varchar(32)");
oid = createTable(SCHEMA, "o", "id int not null primary key, cid int, tag char(1)");
iid = createTable(SCHEMA, "i", "id int not null primary key, spare_id int, tag2 char(1), grouping foreign key(spare_id) references o(id)");
writeRows(
row(cid, 1, "asdf"),
row(cid, 5, "qwer"),
row(cid, 10, "zxcv")
);
writeRows(
row(oid, 10, 1, "a"),
row(oid, 11, 1, "b"),
row(oid, 60, 6, "c")
);
writeRows(
row(iid, 100, 10, "d"),
row(iid, 101, 10, "e"),
row(iid, 102, 10, "f"),
row(iid, 200, 20, "d")
);
runAlter(ChangeLevel.GROUP, "ALTER TABLE o ADD GROUPING FOREIGN KEY(cid) REFERENCES c(id)");
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
RowType cType = schema.tableRowType(getTable(SCHEMA, "c"));
RowType oType = schema.tableRowType(getTable(SCHEMA, "o"));
RowType iType = schema.tableRowType(getTable(SCHEMA, "i"));
StoreAdapter adapter = newStoreAdapter(schema);
compareRows(
new Row[] {
// ?
// null
testRow(iType, 200, 20, "d"),
testRow(cType, 1, "asdf"),
testRow(oType, 10, 1, "a"),
testRow(iType, 100, 10, "d"),
testRow(iType, 101, 10, "e"),
testRow(iType, 102, 10, "f"),
testRow(oType, 11, 1, "b"),
testRow(cType, 5, "qwer"),
// null
testRow(oType, 60, 6, "c"),
testRow(cType, 10, "zxcv"),
},
adapter.newGroupCursor(cType.table().getGroup())
);
}
@Test
public void dropGroupingForeignKeyMiddleOfGroup() throws StandardException {
cid = createTable(SCHEMA, "c", "id int not null primary key, v varchar(32)");
oid = createTable(SCHEMA, "o", "id int not null primary key, cid int, tag char(1), grouping foreign key(cid) references c(id)");
iid = createTable(SCHEMA, "i", "id int not null primary key, spare_id int, tag2 char(1), grouping foreign key(spare_id) references o(id)");
writeRows(
row(cid, 1, "asdf"),
row(cid, 5, "qwer"),
row(cid, 10, "zxcv")
);
writeRows(
row(oid, 10, 1, "a"),
row(oid, 11, 1, "b"),
row(oid, 60, 6, "c")
);
writeRows(
row(iid, 100, 10, "d"),
row(iid, 101, 10, "e"),
row(iid, 102, 10, "f"),
row(iid, 200, 20, "d")
);
runAlter(ChangeLevel.GROUP, "ALTER TABLE o DROP GROUPING FOREIGN KEY");
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
RowType cType = schema.tableRowType(getTable(SCHEMA, "c"));
RowType oType = schema.tableRowType(getTable(SCHEMA, "o"));
RowType iType = schema.tableRowType(getTable(SCHEMA, "i"));
StoreAdapter adapter = newStoreAdapter(schema);
compareRows(
new Row[] {
testRow(cType, 1, "asdf"),
testRow(cType, 5, "qwer"),
testRow(cType, 10, "zxcv")
},
adapter.newGroupCursor(cType.table().getGroup())
);
compareRows(
new Row[] {
testRow(oType, 10, 1, "a"),
testRow(iType, 100, 10, "d"),
testRow(iType, 101, 10, "e"),
testRow(iType, 102, 10, "f"),
testRow(oType, 11, 1, "b"),
// none
testRow(iType, 200, 20, "d"),
testRow(oType, 60, 6, "c"),
},
adapter.newGroupCursor(oType.table().getGroup())
);
}
// bug1037308, part 1
@Test
public void dropColumnConflatedPKFKOnLeaf() {
createTable(
SCHEMA, "customers",
"cid INT NOT NULL PRIMARY KEY",
"name VARCHAR(32) NOT NULL"
);
createTable(
SCHEMA, "orders",
"oid INT NOT NULL PRIMARY KEY",
"cid INT NOT NULL",
"GROUPING FOREIGN KEY(cid) REFERENCES customers(cid)",
"order_date DATE NOT NULL"
);
createTable(
SCHEMA, "items",
"iid INT NOT NULL PRIMARY KEY",
"oid INT NOT NULL",
"GROUPING FOREIGN KEY(oid) REFERENCES orders(oid)",
"sku VARCHAR(32) NOT NULL",
"quan INT NOT NULL"
);
createTable(
SCHEMA, "item_details",
"iid INT NOT NULL PRIMARY KEY",
"GROUPING FOREIGN KEY(iid) REFERENCES items(iid)",
"details VARCHAR(1024)"
);
// Hit assert in sort index size validation
runAlter(ChangeLevel.GROUP, "ALTER TABLE item_details DROP COLUMN iid");
}
// bug1037308, part 2
@Test
public void dropColumnCascadingKeyFromMiddleOfGroup() {
createTable(
SCHEMA, "customers",
"cid INT NOT NULL PRIMARY KEY",
"name VARCHAR(32) NOT NULL"
);
createTable(
SCHEMA, "orders",
"oid INT NOT NULL",
"cid INT NOT NULL",
"PRIMARY KEY(cid, oid)",
"GROUPING FOREIGN KEY(cid) REFERENCES customers(cid)",
"order_date DATE NOT NULL"
);
createTable(
SCHEMA, "items",
"iid INT NOT NULL",
"oid INT NOT NULL",
"cid INT NOT NULL",
"PRIMARY KEY(cid, oid, iid)",
"GROUPING FOREIGN KEY(cid,oid) REFERENCES orders(cid,oid)",
"sku VARCHAR(32) NOT NULL",
"quan INT NOT NULL"
);
// Hit assert in index size validation
runAlter(ChangeLevel.GROUP, "ALTER TABLE orders DROP COLUMN cid");
}
// bug1037387
@Test
public void alterTableWithDefaults() {
createTable(
SCHEMA, C_TABLE,
"cid int not null generated by default as identity primary key",
"c1 varchar(32) default 'bob'",
"c2 int default 42",
"c3 decimal(5,2) default 0.0",
"c4 char(1) default 'N'"
);
LOG.warn("Expecting message for NO_CHANGE alter:");
// First example of the failure in the bug
runAlter(ChangeLevel.NONE, "ALTER TABLE c ALTER COLUMN cid NOT NULL");
// Exception from validator due to defaults incorrectly changing
runAlter("ALTER TABLE c ADD family_size int");
Table table = getTable(C_NAME);
assertEquals("cid default identity", true, table.getColumn("cid").getDefaultIdentity());
assertEquals("c1 default", "bob", table.getColumn("c1").getDefaultValue());
assertEquals("c2 default", "42", table.getColumn("c2").getDefaultValue());
assertEquals("c3 default", "0.0", table.getColumn("c3").getDefaultValue());
assertEquals("c4 default", "N", table.getColumn("c4").getDefaultValue());
assertEquals("family_size default", null, table.getColumn("family_size").getDefaultValue());
}
// bug1037387
@Test
public void modifyColumnPosition() {
createTable(SCHEMA, C_TABLE, "c1 int not null primary key, c2 int");
createIndex(SCHEMA, C_TABLE, "c2", "c2");
int cid = tableId(C_NAME);
writeRows(
row(cid, 1, 10),
row(cid, 2, 20),
row(cid, 3, 30)
);
TestAISBuilder builder = new TestAISBuilder(typesRegistry());
builder.table(SCHEMA, C_TABLE);
builder.column(SCHEMA, C_TABLE, "c2", 0, "MCOMPAT", "int", true);
builder.column(SCHEMA, C_TABLE, "c1", 1, "MCOMPAT", "int", false);
builder.pk(SCHEMA, C_TABLE);
builder.indexColumn(SCHEMA, C_TABLE, Index.PRIMARY, "c1", 0, true, null);
builder.index(SCHEMA, C_TABLE, "c2");
builder.indexColumn(SCHEMA, C_TABLE, "c2", "c2", 0, true, null);
builder.basicSchemaIsComplete();
builder.createGroup(C_TABLE, SCHEMA);
builder.addTableToGroup(C_NAME, SCHEMA, C_TABLE);
builder.groupingIsComplete();
runAlter(ChangeLevel.TABLE,
C_NAME, builder.akibanInformationSchema().getTable(C_NAME),
Arrays.asList(TableChange.createModify("c1", "c1"), TableChange.createModify("c2", "c2")),
NO_CHANGES);
expectRows(
cid,
row(cid, 10, 1),
row(cid, 20, 2),
row(cid, 30, 3)
);
// Let base class check index contents
checkIndexesInstead(C_NAME, "PRIMARY", "c2");
}
// bug1038212
@Test
public void extendVarcharColumnWithIndex() {
cid = createTable(SCHEMA, C_TABLE, "id int not null primary key, state varchar(2)");
createIndex(SCHEMA, C_TABLE, "state", "state");
Row[] rows = {
row(cid, 1, "AZ"),
row(cid, 2, "NY"),
row(cid, 3, "MA"),
row(cid, 4, "WA")
};
writeRows(rows);
runAlter(ChangeLevel.TABLE, "ALTER TABLE c ALTER COLUMN state SET DATA TYPE varchar(3)");
expectRows(cid, rows);
checkIndexesInstead(C_NAME, "PRIMARY", "state");
}
// bug1040347
@Test
public void changeDataTypeDropsSequence() {
final int id = createTable(SCHEMA, C_TABLE,
"id INT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY",
"name VARCHAR(255) NOT NULL");
Sequence seq = getTable(id).getColumn("id").getIdentityGenerator();
assertNotNull("id column has sequence", seq);
writeRows(
row(id, 1, "1"),
row(id, 2, "2"),
row(id, 3, "3")
);
runAlter(ChangeLevel.GROUP, "ALTER TABLE c ALTER COLUMN id SET DATA TYPE varchar(10)");
assertNull("sequence was dropped", ddl().getAIS(session()).getSequence(seq.getSequenceName()));
assertNull("id column has no sequence", getTable(id).getColumn("id").getIdentityGenerator());
expectRows(
id,
row(id, "1", "1"),
row(id, "2", "2"),
row(id, "3", "3")
);
checkIndexesInstead(C_NAME, "PRIMARY");
}
// bug1040347
@Test
public void dropColumnDropsSequence() {
final int id = createTable(SCHEMA, C_TABLE,
"id INT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY",
"name VARCHAR(255) NOT NULL");
Sequence seq = getTable(id).getColumn("id").getIdentityGenerator();
assertNotNull("id column has sequence", seq);
writeRows(
row(id, 1, "1"),
row(id, 2, "2"),
row(id, 3, "3")
);
runAlter(ChangeLevel.GROUP, "ALTER TABLE c DROP COLUMN id");
assertNull("sequence was dropped", ddl().getAIS(session()).getSequence(seq.getSequenceName()));
assertNull("id column does not exist", getTable(id).getColumn("id"));
expectRowsSkipInternal(
id,
row(id, "1"),
row(id, "2"),
row(id, "3")
);
checkIndexesInstead(C_NAME);
}
// bug1046793
@Test
public void dropColumnAutoDropsGroupIndex() {
createAndLoadCOI();
createLeftGroupIndex(C_NAME, "c1_01", "c.c1", "o.o1");
runAlter(ChangeLevel.TABLE, "ALTER TABLE o DROP COLUMN o1");
assertEquals("Remaining group indexes", "[]", ddl().getAIS(session()).getGroup(C_NAME).getIndexes().toString());
checkIndexesInstead(C_NAME, "PRIMARY");
checkIndexesInstead(O_NAME, "PRIMARY");
checkIndexesInstead(I_NAME, "PRIMARY");
}
// bug1047090
@Test
public void changeColumnAffectGroupIndexMultiRootTablesSameName() {
final String schema1 = "test1";
final String schema2 = "test2";
createAndLoadCOI(schema1);
createAndLoadCOI(schema2);
TableName groupName = getTable(schema2, "c").getGroup().getName();
createLeftGroupIndex(groupName, "c1_01", "c.c1", "o.o1");
runAlter(ChangeLevel.TABLE, "ALTER TABLE test2.o ALTER COLUMN o1 SET DATA TYPE bigint");
Index gi = getTable(schema2, "c").getGroup().getIndex("c1_01");
assertNotNull("GI still exists", gi);
IndexRowType type = indexRowType(gi);
scanAndCheckIndex(type,
testRow(type, "a", 11L, 1L, 10L),
testRow(type, "a", 12L, 1L, 11L)
);
}
@Test
public void overlappingFKThenGFK() {
overlappingFKAndGFK(true);
}
@Test
public void overlappingGFKThenFK() {
overlappingFKAndGFK(false);
}
private void overlappingFKAndGFK(boolean fkFirst) {
createTable(SCHEMA, "parent", "pid INT NOT NULL PRIMARY KEY");
createTable(SCHEMA, "child", "cid INT NOT NULL PRIMARY KEY, pid INT");
boolean doFK = fkFirst;
for(int i = 0; i < 2; ++i) {
if(doFK) {
runAlter(ChangeLevel.INDEX_CONSTRAINT, "ALTER TABLE child ADD FOREIGN KEY(pid) REFERENCES parent(pid)");
} else {
runAlter(ChangeLevel.GROUP, "ALTER TABLE child ADD GROUPING FOREIGN KEY(pid) REFERENCES parent(pid)");
}
doFK = !doFK;
}
Table p = ais().getTable(SCHEMA, "parent");
Table c = ais().getTable(SCHEMA, "child");
assertNotNull(p);
assertNotNull(c);
assertEquals(p.getGroup(), c.getGroup());
assertEquals(1, p.getReferencedForeignKeys().size());
assertEquals(1, c.getReferencingForeignKeys().size());
}
@Test(expected=UnsupportedSQLException.class)
public void addGroupIndex() {
createTable(SCHEMA, "parent", "pid INT PRIMARY KEY, x INT");
createTable(SCHEMA, "child", "cid INT PRIMARY KEY, pid INT, y INT, GROUPING FOREIGN KEY(pid) REFERENCES parent");
runAlter(ChangeLevel.INDEX, "ALTER TABLE child ADD INDEX g_i(parent.x, child.y) USING LEFT JOIN");
}
public void changeColumnInGICommon(String table, Runnable alterRunnable) {
String giName = "c1_o1_i1";
createAndLoadCOI();
TableName groupName = getTable(SCHEMA, table).getGroup().getName();
createLeftGroupIndex(groupName, giName, "c.c1", "o.o1", "i.i1");
alterRunnable.run();
Index gi = getTable(SCHEMA, table).getGroup().getIndex(giName);
assertNotNull("GI still exists", gi);
IndexRowType type = indexRowType(gi);
scanAndCheckIndex(type,
testRow(type, "a", 11L, 110L, 1L, 10L, 100L),
testRow(type, "a", 11L, 111L, 1L, 10L, 101L),
testRow(type, "a", 12L, 122L, 1L, 11L, 111L)
);
}
public void changeColumnTypeInGI(final String table, final String column, final String newType) {
changeColumnInGICommon(table, new Runnable() {
@Override
public void run() {
runAlter(ChangeLevel.TABLE,
String.format("ALTER TABLE %s ALTER COLUMN %s SET DATA TYPE %s", table, column, newType));
}
});
}
public void changeColumnNameInGI(final String table, final String column, final String newName) {
changeColumnInGICommon(table, new Runnable() {
@Override
public void run() {
runRenameColumn(new TableName(SCHEMA, table), column, newName);
}
});
}
@Test
public void changeColumnTypeInGI_C() {
changeColumnTypeInGI("c", "c1", "char(10)");
}
@Test
public void changeColumnTypeInGI_O() {
changeColumnTypeInGI("o", "o1", "bigint");
}
@Test
public void changeColumnTypeInGI_I() {
changeColumnTypeInGI("i", "i1", "bigint");
}
@Test
public void changeColumnNameInGI_C() {
changeColumnNameInGI("c", "c1", "c1_new");
}
@Test
public void changeColumnNameInGI_O() {
changeColumnNameInGI("o", "o1", "o1_new");
}
@Test
public void changeColumnNameInGI_I() {
changeColumnNameInGI("i", "i1", "i1_new");
}
@Test
public void alterColumnDefaultIdentity() {
final int id = createTable(SCHEMA, C_TABLE,
"id INT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY");
Column column = getTable(id).getColumn("id");
assertEquals("identity is default", true, column.getDefaultIdentity());
Sequence seq = column.getIdentityGenerator();
assertNotNull("id column has sequence", seq);
runAlter(ChangeLevel.METADATA, "ALTER TABLE c ALTER COLUMN id DROP DEFAULT");
assertNull("Old seq was dropped", ais().getSequence(seq.getSequenceName()));
runAlter(ChangeLevel.METADATA, "ALTER TABLE c ALTER COLUMN id SET GENERATED ALWAYS AS IDENTITY");
Column newColumn = getTable(id).getColumn("id");
assertEquals("altered is always", false, newColumn.getDefaultIdentity());
seq = newColumn.getIdentityGenerator();
assertEquals("Sequence name suffix",
true,
seq.getSequenceName().getTableName().endsWith("_seq"));
}
@Test
public void alterNoPKAddGroupingFK() {
int cid = createTable(SCHEMA, C_TABLE, "id INT NOT NULL PRIMARY KEY, a CHAR(5)");
int oid = createTable(SCHEMA, O_TABLE, "b CHAR(5), cid INT");
writeRows(row(cid, 1L, "a"),
row(cid, 2L, "b"),
row(cid, 3L, "c"),
row(cid, 4L, "d"));
writeRows(row(oid, "aa", 1L),
row(oid, "bb", 2L),
row(oid, "cc", 3L));
runAlter(ChangeLevel.GROUP, "ALTER TABLE o ADD GROUPING FOREIGN KEY(cid) REFERENCES c(id)");
// Check for a hidden PK generator in a bad state (e.g. reproducing old values)
writeRows(row(oid, "dd", 4L));
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
RowType cType = schema.tableRowType(getTable(SCHEMA, C_TABLE));
RowType oType = schema.tableRowType(getTable(SCHEMA, O_TABLE));
StoreAdapter adapter = newStoreAdapter(schema);
compareRows(
new Row[] {
testRow(cType, 1L, "a"),
testRow(oType, "aa", 1L, 1L),
testRow(cType, 2L, "b"),
testRow(oType, "bb", 2L, 2L),
testRow(cType, 3L, "c"),
testRow(oType, "cc", 3L, 3L),
testRow(cType, 4L, "d"),
testRow(oType, "dd", 4L, 4L),
},
adapter.newGroupCursor(cType.table().getGroup())
);
}
@Test
public void renameColumnWithNoPK() {
int cid = createTable(SCHEMA, C_TABLE, "n char(1)");
writeRows(row(cid, "a"),
row(cid, "b"),
row(cid, "c"),
row(cid, "d"));
runAlter(ChangeLevel.METADATA, "ALTER TABLE c RENAME COLUMN \"n\" TO \"n2\"");
// Check for a hidden PK generator in a bad state (e.g. reproducing old values)
writeRows(row(cid, "e"));
expectRowsSkipInternal(
cid,
row(cid, "a"),
row(cid, "b"),
row(cid, "c"),
row(cid, "d"),
// inserted after alter
row(cid, "e")
);
}
@Test
public void addColumnToPKLessTable() {
int cid = createTable(SCHEMA, C_TABLE, "s char(1)");
writeRows(row(cid, "a"),
row(cid, "b"),
row(cid, "c"),
row(cid, "d"));
runAlter(ChangeLevel.TABLE, "ALTER TABLE c ADD COLUMN n INT DEFAULT 0");
writeRows(row(cid, "e", 3));
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
TableRowType cType = schema.tableRowType(getTable(SCHEMA, C_TABLE));
StoreAdapter adapter = newStoreAdapter(schema);
long pk = 1L;
compareRows(
new Row[]{
testRow(cType, "a", 0, pk++),
testRow(cType, "b", 0, pk++),
testRow(cType, "c", 0, pk++),
testRow(cType, "d", 0, pk++),
testRow(cType, "e", 3, pk++),
},
adapter.newGroupCursor(cType.table().getGroup())
);
}
@Test
public void dropPKColumn() {
int cid = createTable(SCHEMA, C_TABLE, "s char(1), n int not null primary key");
writeRows(row(cid, "a", 1),
row(cid, "b", 2),
row(cid, "c", 3),
row(cid, "d", 4));
runAlter(ChangeLevel.GROUP, "ALTER TABLE c DROP COLUMN n");
writeRows(row(cid, "e"));
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
TableRowType cType = schema.tableRowType(getTable(SCHEMA, C_TABLE));
StoreAdapter adapter = newStoreAdapter(schema);
long pk = 1L;
compareRows(
new Row[]{
testRow(cType, "a", pk++),
testRow(cType, "b", pk++),
testRow(cType, "c", pk++),
testRow(cType, "d", pk++),
testRow(cType, "e", pk++),
},
adapter.newGroupCursor(cType.table().getGroup())
);
}
@Test
public void addPKColumnToPKLessTable() {
int cid = createTable(SCHEMA, C_TABLE, "s char(1)");
writeRows(row(cid, "a"),
row(cid, "b"),
row(cid, "c"),
row(cid, "d"));
runAlter(ChangeLevel.GROUP, "ALTER TABLE c ADD COLUMN n SERIAL PRIMARY KEY");
// writerows doesn't run default handling behavior
writeRows(row(cid, "e", 5));
expectRows(
cid,
row(cid, "a", 1),
row(cid, "b", 2),
row(cid, "c", 3),
row(cid, "d", 4),
// inserted after alter
row(cid, "e", 5)
);
}
@Test
public void addPKToPKLessTable() {
int cid = createTable(SCHEMA, C_TABLE, "n char(1) NOT NULL");
writeRows(row(cid, "a"),
row(cid, "b"),
row(cid, "c"),
row(cid, "d"));
runAlter(ChangeLevel.GROUP, "ALTER TABLE c ADD PRIMARY KEY(n)");
// Check for a hidden PK generator in a bad state (e.g. reproducing old values)
writeRows(row(cid, "e"));
expectRows(
cid,
row(cid, "a"),
row(cid, "b"),
row(cid, "c"),
row(cid, "d"),
// inserted after alter
row(cid, "e")
);
}
@Test
public void alterPKlessTableWithIndex() {
// This changes an index, and does a TABLE change, but not a GROUP change
int cid = createTable(SCHEMA, C_TABLE, "a char(1) NOT NULL, b char(1) NOT NULL");
createIndex(SCHEMA, C_TABLE, "a_index", "a");
writeRows(row(cid, "a", "z"),
row(cid, "b", "y"),
row(cid, "c", "x"),
row(cid, "d", "w"));
runAlter(ChangeLevel.TABLE, "ALTER TABLE c ALTER a SET DATA TYPE varchar(3)");
// Check for a hidden PK generator in a bad state (e.g. reproducing old values)
writeRows(row(cid, "e", "v"));
expectRowsSkipInternal(
cid,
row(cid, "a", "z"),
row(cid, "b", "y"),
row(cid, "c", "x"),
row(cid, "d", "w"),
// inserted after alter
row(cid, "e", "v")
);
}
@Test(expected=NoColumnsInTableException.class)
public void alterDropsAllColumns() {
createTable(SCHEMA, "t", "c1 int");
runAlter("ALTER TABLE t DROP COLUMN c1");
}
}