Package com.foundationdb.server.test.it.dxl

Source Code of com.foundationdb.server.test.it.dxl.AlterTableBasicIT

/**
* 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");
    }
}
TOP

Related Classes of com.foundationdb.server.test.it.dxl.AlterTableBasicIT

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.