Ejemplo n.º 1
0
 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());
       }
     }
   }
 }
Ejemplo n.º 2
0
  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);
  }