Package com.foundationdb.sql.aisddl

Source Code of com.foundationdb.sql.aisddl.AlterTableDDL$TableGroupWithoutIndexesSelector

/**
* 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.sql.aisddl;

import com.foundationdb.ais.model.Sequence;
import com.foundationdb.server.error.ColumnAlreadyGeneratedException;
import com.foundationdb.server.error.ColumnNotGeneratedException;
import com.foundationdb.server.error.ProtectedColumnDDLException;
import com.foundationdb.server.error.SQLParserInternalException;
import com.foundationdb.sql.parser.AlterDropIndexNode;
import com.foundationdb.sql.parser.AlterTableRenameColumnNode;
import com.foundationdb.sql.parser.AlterTableRenameNode;
import com.foundationdb.ais.AISCloner;
import com.foundationdb.ais.model.AISBuilder;
import com.foundationdb.ais.model.AkibanInformationSchema;
import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.Columnar;
import com.foundationdb.ais.model.DefaultIndexNameGenerator;
import com.foundationdb.ais.model.ForeignKey;
import com.foundationdb.ais.model.Index;
import com.foundationdb.ais.model.IndexColumn;
import com.foundationdb.ais.model.IndexName;
import com.foundationdb.ais.model.IndexNameGenerator;
import com.foundationdb.ais.model.Join;
import com.foundationdb.ais.model.Routine;
import com.foundationdb.ais.model.SQLJJar;
import com.foundationdb.ais.model.Table;
import com.foundationdb.ais.model.TableIndex;
import com.foundationdb.ais.model.TableName;
import com.foundationdb.ais.protobuf.ProtobufWriter;
import com.foundationdb.ais.util.TableChange;
import com.foundationdb.ais.util.TableChange.ChangeType;
import com.foundationdb.qp.operator.QueryContext;
import com.foundationdb.server.api.DDLFunctions;
import com.foundationdb.server.api.DMLFunctions;
import com.foundationdb.server.error.ForeignKeyPreventsAlterColumnException;
import com.foundationdb.server.error.JoinToMultipleParentsException;
import com.foundationdb.server.error.NoSuchColumnException;
import com.foundationdb.server.error.NoSuchConstraintException;
import com.foundationdb.server.error.NoSuchForeignKeyException;
import com.foundationdb.server.error.NoSuchGroupingFKException;
import com.foundationdb.server.error.NoSuchIndexException;
import com.foundationdb.server.error.NoSuchTableException;
import com.foundationdb.server.error.NoSuchUniqueException;
import com.foundationdb.server.error.UnsupportedCheckConstraintException;
import com.foundationdb.server.error.UnsupportedFKIndexException;
import com.foundationdb.server.error.UnsupportedSQLException;
import com.foundationdb.server.service.session.Session;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.server.types.common.types.TypesTranslator;
import com.foundationdb.sql.StandardException;
import com.foundationdb.sql.parser.AlterTableNode;
import com.foundationdb.sql.parser.ColumnDefinitionNode;
import com.foundationdb.sql.parser.ConstraintDefinitionNode;
import com.foundationdb.sql.parser.FKConstraintDefinitionNode;
import com.foundationdb.sql.parser.IndexDefinitionNode;
import com.foundationdb.sql.parser.ModifyColumnNode;
import com.foundationdb.sql.parser.NodeTypes;
import com.foundationdb.sql.parser.QueryTreeNode;
import com.foundationdb.sql.parser.StatementType;
import com.foundationdb.sql.parser.TableElementList;
import com.foundationdb.sql.parser.TableElementNode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.LoggerFactory;

import static com.foundationdb.ais.util.TableChangeValidator.ChangeLevel;
import static com.foundationdb.sql.aisddl.DDLHelper.convertName;
import static com.foundationdb.sql.aisddl.DDLHelper.skipOrThrow;
import static com.foundationdb.sql.parser.ConstraintDefinitionNode.ConstraintType;

public class AlterTableDDL {
    private AlterTableDDL() {}

    public static ChangeLevel alterTable(DDLFunctions ddlFunctions,
                                         DMLFunctions dmlFunctions,
                                         Session session,
                                         String defaultSchemaName,
                                         AlterTableNode alterTable,
                                         QueryContext context) {
        final AkibanInformationSchema curAIS = ddlFunctions.getAIS(session);
        final TableName tableName = convertName(defaultSchemaName, alterTable.getObjectName());
        final Table table = curAIS.getTable(tableName);
        if((table == null) &&
           skipOrThrow(context, alterTable.getExistenceCheck(), null, new NoSuchTableException(tableName))) {
            return null;
        }

        if (alterTable.isUpdateStatistics()) {
            Collection<String> indexes = null;
            if (!alterTable.isUpdateStatisticsAll())
                indexes = Collections.singletonList(alterTable.getIndexNameForUpdateStatistics());
            ddlFunctions.updateTableStatistics(session, tableName, indexes);
            return null;
        }

        if (alterTable.isTruncateTable()) {
            dmlFunctions.truncateTable(session, table.getTableId(), alterTable.isCascade());
            return null;
        }

        ChangeLevel level = null;
        if((alterTable.tableElementList != null) && !alterTable.tableElementList.isEmpty()) {
            level = processAlter(ddlFunctions, session, defaultSchemaName, table, alterTable.tableElementList, context);
        }

        if(level == null) {
            throw new UnsupportedSQLException (alterTable.statementToString(), alterTable);
        }
        return level;
    }

    private static ChangeLevel processAlter(DDLFunctions ddl,
                                            Session session,
                                            String defaultSchema,
                                            Table origTable,
                                            TableElementList elements,
                                            QueryContext context) {
        List<TableChange> columnChanges = new ArrayList<>();
        List<TableChange> indexChanges = new ArrayList<>();
        List<ColumnDefinitionNode> columnDefNodes = new ArrayList<>();
        List<FKConstraintDefinitionNode> fkDefNodes= new ArrayList<>();
        List<ConstraintDefinitionNode> conDefNodes = new ArrayList<>();
        List<IndexDefinitionNode> indexDefNodes = new ArrayList<>();

        for(TableElementNode node : elements) {
            switch(node.getNodeType()) {
                case NodeTypes.COLUMN_DEFINITION_NODE: {
                    ColumnDefinitionNode cdn = (ColumnDefinitionNode) node;
                    String columnName = cdn.getColumnName();
                    columnChanges.add(TableChange.createAdd(columnName));
                    if (Column.isInternalName(columnName))
                    {
                        throw new ProtectedColumnDDLException(columnName);
                    }
                    columnDefNodes.add(cdn);
                } break;

                case NodeTypes.DROP_COLUMN_NODE: {
                    String columnName = ((ModifyColumnNode)node).getColumnName();
                    if(Column.isInternalName(columnName) || (origTable.getColumn(columnName) == null)) {
                        skipOrThrow(context, ((ModifyColumnNode)node).getExistenceCheck(), null, new NoSuchColumnException(columnName));
                    } else {
                        columnChanges.add(TableChange.createDrop(columnName));
                    }
                } break;

                case NodeTypes.MODIFY_COLUMN_DEFAULT_NODE:
                case NodeTypes.MODIFY_COLUMN_CONSTRAINT_NODE:
                case NodeTypes.MODIFY_COLUMN_CONSTRAINT_NOT_NULL_NODE:
                case NodeTypes.MODIFY_COLUMN_TYPE_NODE: {
                    ModifyColumnNode modNode = (ModifyColumnNode)node;
                    String columnName = modNode.getColumnName();
                    Column column = origTable.getColumn(columnName);
                    if(Column.isInternalName(columnName) || (column == null)) {
                        skipOrThrow(context, modNode.getExistenceCheck(), null, new NoSuchColumnException(columnName));
                    } else {
                        // Special case: The only requested change is RESTART WITH.
                        // Cannot go through the current ALTER flow as the new value may appear to be the same,
                        // triggering NONE change, but should still take affect as values may have been allocated.
                        if((elements.size() == 1) && isRestartWithNode(modNode)) {
                            Sequence curSeq = column.getIdentityGenerator();
                            if(curSeq == null) {
                                throw new ColumnNotGeneratedException(column);
                            }
                            AkibanInformationSchema aisCopy = new AkibanInformationSchema();
                            Sequence newSeq = Sequence.create(aisCopy,
                                                              curSeq.getSchemaName(),
                                                              curSeq.getSequenceName().getTableName(),
                                                              modNode.getAutoincrementStart(),
                                                              curSeq.getIncrement(),
                                                              curSeq.getMinValue(),
                                                              curSeq.getMaxValue(),
                                                              curSeq.isCycle());
                            ddl.alterSequence(session, curSeq.getSequenceName(), newSeq);
                            return ChangeLevel.METADATA;
                        } else {
                            columnChanges.add(TableChange.createModify(columnName, columnName));
                            columnDefNodes.add((ColumnDefinitionNode)node);
                        }
                    }
                } break;

                case NodeTypes.FK_CONSTRAINT_DEFINITION_NODE: {
                    FKConstraintDefinitionNode fkNode = (FKConstraintDefinitionNode) node;
                    if(fkNode.getConstraintType() == ConstraintType.DROP) {
                        if(fkNode.isGrouping()) {
                            if(origTable.getParentJoin() == null) {
                                skipOrThrow(context, fkNode.getExistenceCheck(), null, new NoSuchGroupingFKException(origTable.getName()));
                                fkNode = null;
                            }
                        } else {
                            if(fkNode.getConstraintName() == null) {
                                Collection<ForeignKey> fkeys = origTable.getReferencingForeignKeys();
                                if(fkeys.size() == 0) {
                                    skipOrThrow(context, fkNode.getExistenceCheck(), null, new UnsupportedFKIndexException());
                                    fkNode = null;
                                } else if(fkeys.size() != 1) {
                                    throw new UnsupportedFKIndexException();
                                } else {
                                    try {
                                        fkNode.setConstraintName(fkeys.iterator().next().getConstraintName().getTableName());
                                    } catch(StandardException ex) {
                                        throw new SQLParserInternalException(ex);
                                    }
                                }
                            } else if(origTable.getReferencingForeignKey(fkNode.getConstraintName().getTableName()) == null) {
                                skipOrThrow(context,
                                            fkNode.getExistenceCheck(),
                                            null,
                                            new NoSuchForeignKeyException(fkNode.getConstraintName().getTableName(), origTable.getName()));
                                fkNode = null;
                            }
                            if(fkNode != null) {
                                // Also drop the referencing index.
                                indexChanges.add(TableChange.createDrop(fkNode.getConstraintName().getTableName()));
                            }
                        }
                    }
                    if(fkNode != null) {
                        fkDefNodes.add(fkNode);
                    }
                } break;

                case NodeTypes.CONSTRAINT_DEFINITION_NODE: {
                    ConstraintDefinitionNode cdn = (ConstraintDefinitionNode) node;
                    if(cdn.getConstraintType() == ConstraintType.DROP) {
                        String name = cdn.getName();
                        switch(cdn.getVerifyType()) {
                            case PRIMARY_KEY:
                                if(origTable.getPrimaryKey() == null) {
                                    skipOrThrow(context, cdn.getExistenceCheck(),
                                                null,
                                                new NoSuchConstraintException(origTable.getName(), Index.PRIMARY));
                                    name = null;
                                } else {
                                    name = origTable.getPrimaryKey().getIndex().getIndexName().getName();
                                }
                            break;
                            case DROP:
                                boolean found = false;
                                String indexName = indexNameForConstrainName(origTable, name);
                                if (indexName != null) {
                                    found = true;
                                    name = indexName;
                                } else if (origTable.getReferencingForeignKey(name) != null) {
                                    fkDefNodes.add(newFKDropNode(node, name, Boolean.FALSE));
                                    found = true;
                                } else if (origTable.getParentJoin() != null && origTable.getParentJoin().getName().equals(name)) {
                                    fkDefNodes.add(newFKDropNode(node, name, Boolean.TRUE));
                                    found = true;
                                    name = null;
                                }
                                if(!found) {
                                   skipOrThrow(context,
                                               cdn.getExistenceCheck(),
                                               null,
                                               new NoSuchConstraintException(origTable.getName(), name));
                                    name = null;
                                }
                                break;
                            case UNIQUE:
                                Index index = origTable.getIndex(name);
                                if(index == null || !index.isUnique()) {
                                    skipOrThrow(context,
                                                cdn.getExistenceCheck(),
                                                null,
                                                new NoSuchUniqueException(origTable.getName(), cdn.getName()));
                                    name = null;
                                }
                            break;
                            case CHECK:
                                throw new UnsupportedCheckConstraintException();
                        }
                        if (name != null) {
                            indexChanges.add(TableChange.createDrop(name));
                        }
                    } else if (cdn.getConstraintType() == ConstraintType.PRIMARY_KEY) {
                        if (origTable.getPrimaryKeyIncludingInternal().isAkibanPK())
                        {
                            columnChanges.add(TableChange.createDrop(Column.ROW_ID_NAME));
                            String indexName = origTable.getPrimaryKeyIncludingInternal().getIndex().getIndexName().getName();
                            indexChanges.add(TableChange.createDrop(indexName));
                        }
                        conDefNodes.add(cdn);
                    } else {
                        conDefNodes.add(cdn);
                    }
                } break;

                case NodeTypes.INDEX_DEFINITION_NODE:
                    IndexDefinitionNode idn = (IndexDefinitionNode)node;
                    if(idn.getJoinType() != null) {
                        throw new UnsupportedSQLException("ALTER ADD INDEX containing group index");
                    }
                    indexDefNodes.add(idn);
                    break;
                   
                case NodeTypes.AT_DROP_INDEX_NODE: {
                    AlterDropIndexNode dropIndexNode = (AlterDropIndexNode)node;
                    String name = dropIndexNode.getIndexName();
                    if(origTable.getIndex(name) == null) {
                        skipOrThrow(context, dropIndexNode.getExistenceCheck(), null, new NoSuchIndexException(name));
                    } else {
                        indexChanges.add(TableChange.createDrop(name));
                    }
                }
                break;

                case NodeTypes.AT_RENAME_NODE:
                    TableName newName = DDLHelper.convertName(defaultSchema,
                                                              ((AlterTableRenameNode)node).newName());
                    TableName oldName = origTable.getName();
                    ddl.renameTable(session, oldName, newName);
                    return ChangeLevel.METADATA;
                   
                case NodeTypes.AT_RENAME_COLUMN_NODE:
                    AlterTableRenameColumnNode alterRenameCol = (AlterTableRenameColumnNode) node;
                    String oldColName = alterRenameCol.getName();
                    String newColName = alterRenameCol.newName();
                    final Column oldCol = origTable.getColumn(oldColName);

                    if (oldCol == null) {
                        throw new NoSuchColumnException(oldColName);
                    }
                    columnChanges.add(TableChange.createModify(oldColName, newColName));
                break;

                default:
                    return null; // Something unsupported
            }
        }
       
        for (ForeignKey foreignKey : origTable.getForeignKeys()) {
            if (foreignKey.getReferencingTable() == origTable) {
                checkForeignKeyAlterColumns(columnChanges, foreignKey.getReferencingColumns(),
                                            foreignKey, origTable);
            }
            if (foreignKey.getReferencedTable() == origTable) {
                checkForeignKeyAlterColumns(columnChanges, foreignKey.getReferencedColumns(),
                                            foreignKey, origTable);
            }
        }

        final AkibanInformationSchema origAIS = origTable.getAIS();
        final Table tableCopy = copyTable(ddl.getAISCloner(), origTable, columnChanges);
        final AkibanInformationSchema aisCopy = tableCopy.getAIS();
        TableDDL.cloneReferencedTables(defaultSchema, ddl.getAISCloner(), origAIS, aisCopy, elements);
        final TypesTranslator typesTranslator = ddl.getTypesTranslator();
        final AISBuilder builder = new AISBuilder(aisCopy);
        builder.getNameGenerator().mergeAIS(origAIS);

        int pos = tableCopy.getColumnsIncludingInternal().size();
        for(ColumnDefinitionNode cdn : columnDefNodes) {
            if(cdn instanceof ModifyColumnNode) {
                ModifyColumnNode modNode = (ModifyColumnNode) cdn;
                handleModifyColumnNode(modNode, builder, tableCopy, typesTranslator);
            } else {
                TableDDL.addColumn(builder, typesTranslator, cdn, origTable.getName().getSchemaName(), origTable.getName().getTableName(), pos++);
            }
        }
        // Ensure that internal columns stay at the end
        // because there's a bunch of places that assume that they are
        // (e.g. they assume getColumns() have indexes (1...getColumns().size()))
        // If the original table had a primary key, the hidden pk is added a bit farther down
       
        for (Column origColumn : origTable.getColumnsIncludingInternal()) {
            if (origColumn.isInternalColumn()) {
                String newName = findNewName(columnChanges, origColumn.getName());
                if (newName != null) {
                    Column.create(tableCopy, origColumn, newName, pos++);
                }
            }
        }
        copyTableIndexes(origTable, tableCopy, columnChanges, indexChanges);

        IndexNameGenerator indexNamer = DefaultIndexNameGenerator.forTable(tableCopy);
        TableName newName = tableCopy.getName();
        for(ConstraintDefinitionNode cdn : conDefNodes) {
            assert cdn.getConstraintType() != ConstraintType.DROP : cdn;
            String name = TableDDL.addIndex(indexNamer, builder, cdn, newName.getSchemaName(), newName.getTableName(), context);
            indexChanges.add(TableChange.createAdd(name));
            // This is required as the addIndex() for a primary key constraint
            // *may* alter the NULL->NOT NULL status
            // of the columns in the primary key
            if (name.equals(Index.PRIMARY)) {
                for (IndexColumn col : tableCopy.getIndex(name).getKeyColumns())
                {
                    String columnName = col.getColumn().getName();
                   
                    // Check if the column was added in the same alter as creating the index:
                    // ALTER TABLE c ADD COLUMN n SERIAL PRIMARY KEY
                    // You can't add and modify the column, so assume the add does the correct thing.
                    boolean columnAdded = false;
                    for (TableChange change : columnChanges) {
                        if (change.getChangeType() ==  ChangeType.ADD && columnName.equals(change.getNewName()))
                            columnAdded = true;
                    }
                    if (!columnAdded)
                        columnChanges.add(TableChange.createModify(columnName, columnName));
                }
            }
        }

        for(IndexDefinitionNode idn : indexDefNodes) {
            String name = TableDDL.addIndex(indexNamer, builder, idn, newName.getSchemaName(), newName.getTableName(), context, ddl);
            indexChanges.add(TableChange.createAdd(name));
        }

        // Correctly adds the Hidden PK (including sequence).
        if (tableCopy.getPrimaryKeyIncludingInternal() == null) {
            if (origTable.getPrimaryKeyIncludingInternal().isAkibanPK()) {
                Column origColumn = origTable.getPrimaryKeyIncludingInternal().getColumns().get(0);
                Column.create(tableCopy, origColumn, Column.ROW_ID_NAME, tableCopy.getColumns().size());
            } else {
                tableCopy.addHiddenPrimaryKey(builder.getNameGenerator());
                columnChanges.add(TableChange.createAdd(Column.ROW_ID_NAME));
            }
        }
       
        for(FKConstraintDefinitionNode fk : fkDefNodes) {
            if(fk.isGrouping()) {
                if(fk.getConstraintType() == ConstraintType.DROP) {
                    Join parentJoin = tableCopy.getParentJoin();
                    tableCopy.setGroup(null);
                    tableCopy.removeCandidateParentJoin(parentJoin);
                    parentJoin.getParent().removeCandidateChildJoin(parentJoin);
                } else {
                    if(origTable.getParentJoin() != null) {
                        throw new JoinToMultipleParentsException(origTable.getName());
                    }
                    tableCopy.setGroup(null);
                    TableDDL.addJoin(builder, fk, defaultSchema, newName.getSchemaName(), newName.getTableName());
                }
            } else {
                if(fk.getConstraintType() == ConstraintType.DROP) {
                    String name = fk.getConstraintName().getTableName();
                    ForeignKey tableFK = null;
                    for (ForeignKey tfk : tableCopy.getReferencingForeignKeys()) {
                        if (name.equals(tfk.getConstraintName().getTableName())) {
                            tableFK = tfk;
                            break;
                        }
                    }
                    assert tableFK != null : name;
                    tableCopy.removeForeignKey(tableFK);
                } else {
                    TableDDL.addForeignKey(builder, origAIS, fk, defaultSchema, newName.getSchemaName(), newName.getTableName());
                }
            }
        }
        return ddl.alterTable(session, origTable.getName(), tableCopy, columnChanges, indexChanges, context);
    }

    private static void checkForeignKeyAlterColumns(List<TableChange> columnChanges, Collection<Column> columns, ForeignKey foreignKey, Table table) {
        for (Column column : columns) {
            for (TableChange change : columnChanges) {
                if (column.getName().equals(change.getOldName())) {
                    throw new ForeignKeyPreventsAlterColumnException(column.getName(), table.getName(), foreignKey.getConstraintName().getTableName());
                }
            }
        }
    }

    private static void handleModifyColumnNode(ModifyColumnNode modNode, AISBuilder builder, Table tableCopy, TypesTranslator typesTranslator) {
        AkibanInformationSchema aisCopy = tableCopy.getAIS();
        Column column = tableCopy.getColumn(modNode.getColumnName());
        assert column != null : modNode.getColumnName();
        switch(modNode.getNodeType()) {
            case NodeTypes.MODIFY_COLUMN_DEFAULT_NODE:
                if(modNode.isAutoincrementColumn()) {
                    int autoIncType = (int)modNode.getAutoinc_create_or_modify_Start_Increment();
                    switch(autoIncType) {
                        case ColumnDefinitionNode.CREATE_AUTOINCREMENT: {
                            if(column.getIdentityGenerator() != null) {
                                throw new ColumnAlreadyGeneratedException(column);
                            }
                            TableName name = tableCopy.getName();
                            TableDDL.setAutoIncrement(builder, name.getSchemaName(), name.getTableName(), modNode);
                        }
                        break;
                        case ColumnDefinitionNode.MODIFY_AUTOINCREMENT_INC_VALUE:
                            throw new UnsupportedSQLException("SET INCREMENT BY", modNode);
                        case ColumnDefinitionNode.MODIFY_AUTOINCREMENT_RESTART_VALUE:
                            // Note: Also handled above
                            throw new UnsupportedSQLException("RESTART WITH", modNode);
                        default:
                            throw new IllegalStateException("Unknown autoIncType: " + autoIncType);
                    }
                } else {
                    // DROP DEFAULT will come though as a NULL default, clears both GENERATED and DEFAULT
                    Sequence seq = column.getIdentityGenerator();
                    if(seq != null) {
                        column.setDefaultIdentity(null);
                        column.setIdentityGenerator(null);
                        aisCopy.removeSequence(seq.getSequenceName());
                    }
                    String[] defaultValueFunction = TableDDL.getColumnDefault(modNode, tableCopy.getName().getSchemaName(), tableCopy.getName().getTableName());
                    column.setDefaultValue(defaultValueFunction[0]);
                    column.setDefaultFunction(defaultValueFunction[1]);
                }
            break;
            case NodeTypes.MODIFY_COLUMN_CONSTRAINT_NODE: // Type only comes from NULL
                column.setType(column.getType().withNullable(true));
            break;
            case NodeTypes.MODIFY_COLUMN_CONSTRAINT_NOT_NULL_NODE: // Type only comes from NOT NULL
                column.setType(column.getType().withNullable(false));
            break;
            case NodeTypes.MODIFY_COLUMN_TYPE_NODE: // All but [NOT] NULL comes from type
                {
                    TInstance type = typesTranslator
                        .typeForSQLType(modNode.getType())
                        .withNullable(column.getNullable());
                    if (false) {
                        // TODO: Determine whether compatible, does affect sequence, etc.
                        column.setType(type);
                    }
                    else {
                        tableCopy.dropColumn(modNode.getColumnName());
                        builder.column(tableCopy.getName().getSchemaName(), tableCopy.getName().getTableName(), column.getName(),
                                       column.getPosition(), type, false, // column.getInitialAutoIncrementValue() != null
                                       column.getDefaultValue(), column.getDefaultFunction());
                    }
                }
            break;
            default:
                throw new IllegalStateException("Unexpected node type: " + modNode);
        }
    }

    private static ChangeType findOldName(List<TableChange> changes, String oldName) {
        for(TableChange change : changes) {
            if(oldName.equals(change.getOldName())) {
                return change.getChangeType();
            }
        }
        return null;
    }

    private static String findNewName(List<TableChange> changes, String oldName)
    {
        for(TableChange change : changes) {
            if(oldName.equals(change.getOldName())) {
                return change.getChangeType() == ChangeType.DROP ? null : change.getNewName();
            }
        }
        return oldName;
    }

    private static FKConstraintDefinitionNode newFKDropNode(TableElementNode node,
                                                            String name,
                                                            Boolean isGrouping) {
        try {
            QueryTreeNode fkName = node.getParserContext()
                                       .getNodeFactory()
                                       .getNode(NodeTypes.TABLE_NAME, null, name, node.getParserContext());

            return (FKConstraintDefinitionNode)node.getParserContext()
                                                   .getNodeFactory()
                                                   .getNode(NodeTypes.FK_CONSTRAINT_DEFINITION_NODE,
                                                            fkName,
                                                            ConstraintDefinitionNode.ConstraintType.DROP,
                                                            StatementType.DROP_DEFAULT,
                                                            isGrouping,
                                                            null /*existence*/,
                                                            node.getParserContext());
        } catch(StandardException ex) {
            throw new SQLParserInternalException(ex);
        }
    }

    private static Table copyTable(AISCloner aisCloner, Table origTable, List<TableChange> columnChanges) {
        AkibanInformationSchema aisCopy = aisCloner.clone(origTable.getAIS(), new TableGroupWithoutIndexesSelector(origTable));
        Table tableCopy = aisCopy.getTable(origTable.getName());

        // Remove and recreate. NB: hidden PK/column handled downstream.
        tableCopy.dropColumns();

        int colPos = 0;
        // internal columns are copied after we add new columns
        for(Column origColumn : origTable.getColumns()) {
            String newName = findNewName(columnChanges, origColumn.getName());
            if(newName != null) {
                Column.create(tableCopy, origColumn, newName, colPos++);
            }
        }

        return tableCopy;
    }
   
    private static void copyTableIndexes(Table origTable,
                                         Table tableCopy,
                                         List<TableChange> columnChanges,
                                         List<TableChange> indexChanges) {
        for(TableIndex origIndex : origTable.getIndexesIncludingInternal()) {
            ChangeType indexChange = findOldName(indexChanges, origIndex.getIndexName().getName());
            if(indexChange == ChangeType.DROP) {
                continue;
            }
            TableIndex indexCopy = TableIndex.create(tableCopy, origIndex);
            int pos = 0;
            for(IndexColumn indexColumn : origIndex.getKeyColumns()) {
                String newName = findNewName(columnChanges, indexColumn.getColumn().getName());
                if(newName != null) {
                    IndexColumn.create(indexCopy, tableCopy.getColumn(newName), indexColumn, pos++);
                }
            }
            // DROP and MODIFY detection for indexes handled downstream
            if(indexCopy.getKeyColumns().isEmpty()) {
                tableCopy.removeIndexes(Collections.singleton(indexCopy));
            }
        }
    }
   
    private static String indexNameForConstrainName(Table origTable, String name) {
        for(TableIndex ti : origTable.getIndexes()) {
            if ((ti.getConstraintName() != null) && (ti.getConstraintName().getTableName().equals(name))) {
                return ti.getIndexName().getName();
            }
        }
        return null;
    }

    private static boolean isRestartWithNode(ModifyColumnNode modNode) {
        return (modNode.getNodeType() == NodeTypes.MODIFY_COLUMN_DEFAULT_NODE) &&
                modNode.isAutoincrementColumn() &&
                (modNode.getAutoinc_create_or_modify_Start_Increment() == ColumnDefinitionNode.MODIFY_AUTOINCREMENT_RESTART_VALUE);
    }

    private static class TableGroupWithoutIndexesSelector extends ProtobufWriter.TableSelector {
        private final Table table;
        private final Set<Table> fkTables;

        public TableGroupWithoutIndexesSelector(Table table) {
            this.table = table;
            Collection<ForeignKey> fkeys = table.getReferencingForeignKeys();
            if (fkeys.isEmpty()) {
                fkTables = Collections.emptySet();
            } else {
                fkTables = new HashSet<>(fkeys.size());
                for (ForeignKey fkey : fkeys) {
                    fkTables.add(fkey.getReferencedTable());
                }
            }
        }

        @Override
        public boolean isSelected(Columnar columnar) {
            return columnar.isTable() &&
                ((((Table)columnar).getGroup() == table.getGroup()) ||
                 fkTables.contains(columnar));
        }

        @Override
        public boolean isSelected(Index index) {
            return !(index.isTableIndex() && index.leafMostTable() == table);
        }

        @Override
        public boolean isSelected(Routine routine) {
            return false;
        }

        @Override
        public boolean isSelected(SQLJJar sqljJar) {
            return false;
        }

        @Override
        public boolean isSelected(ForeignKey foreignKey) {
            return (foreignKey.getReferencingTable() == table);
        }
    }
}
TOP

Related Classes of com.foundationdb.sql.aisddl.AlterTableDDL$TableGroupWithoutIndexesSelector

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.