public boolean announceMigration(boolean isLocalOnly) throws RequestValidationException {
    CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), columnFamily()).copy();
    IndexTarget target = rawTarget.prepare(cfm);
    logger.debug("Updating column {} definition for index {}", target.column, indexName);
    ColumnDefinition cd = cfm.getColumnDefinition(target.column);

    if (cd.getIndexType() != null && ifNotExists) return false;

    if (properties.isCustom) {
      cd.setIndexType(IndexType.CUSTOM, properties.getOptions());
    } else if (cfm.comparator.isCompound()) {
      Map<String, String> options = Collections.emptyMap();
      // For now, we only allow indexing values for collections, but we could later allow
      // to also index map keys, so we record that this is the values we index to make our
      // lives easier then.
      if (cd.type.isCollection() && cd.type.isMultiCell())
        options =
            ImmutableMap.of(
                target.isCollectionKeys
                    ? SecondaryIndex.INDEX_KEYS_OPTION_NAME
                    : SecondaryIndex.INDEX_VALUES_OPTION_NAME,
                "");
      cd.setIndexType(IndexType.COMPOSITES, options);
    } else {
      cd.setIndexType(IndexType.KEYS, Collections.<String, String>emptyMap());
    }

    cd.setIndexName(indexName);
    cfm.addDefaultIndexNames();
    MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly);
    return true;
  }
  public void validate(ClientState state) throws RequestValidationException {
    CFMetaData cfm = ThriftValidation.validateColumnFamily(keyspace(), columnFamily());
    if (cfm.isCounter())
      throw new InvalidRequestException("Secondary indexes are not supported on counter tables");

    IndexTarget target = rawTarget.prepare(cfm);
    ColumnDefinition cd = cfm.getColumnDefinition(target.column);

    if (cd == null)
      throw new InvalidRequestException("No column definition found for column " + target.column);

    boolean isMap = cd.type instanceof MapType;
    boolean isFrozenCollection = cd.type.isCollection() && !cd.type.isMultiCell();
    if (target.isCollectionKeys) {
      if (!isMap)
        throw new InvalidRequestException(
            "Cannot create index on keys of column " + target + " with non-map type");
      if (!cd.type.isMultiCell())
        throw new InvalidRequestException(
            "Cannot create index on keys of frozen<map> column " + target);
    } else if (target.isFullCollection) {
      if (!isFrozenCollection)
        throw new InvalidRequestException(
            "full() indexes can only be created on frozen collections");
    } else if (isFrozenCollection) {
      throw new InvalidRequestException(
          "Frozen collections currently only support full-collection indexes. "
              + "For example, 'CREATE INDEX ON <table>(full(<columnName>))'.");
    }

    if (cd.getIndexType() != null) {
      boolean previousIsKeys = cd.hasIndexOption(SecondaryIndex.INDEX_KEYS_OPTION_NAME);
      if (isMap && target.isCollectionKeys != previousIsKeys) {
        String msg =
            "Cannot create index on %s %s, an index on %s %s already exists and indexing "
                + "a map on both keys and values at the same time is not currently supported";
        throw new InvalidRequestException(
            String.format(
                msg,
                target.column,
                target.isCollectionKeys ? "keys" : "values",
                target.column,
                previousIsKeys ? "keys" : "values"));
      }

      if (ifNotExists) return;
      else throw new InvalidRequestException("Index already exists");
    }

    properties.validate();

    // TODO: we could lift that limitation
    if ((cfm.comparator.isDense() || !cfm.comparator.isCompound())
        && cd.kind != ColumnDefinition.Kind.REGULAR)
      throw new InvalidRequestException(
          "Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables");

    // It would be possible to support 2ndary index on static columns (but not without modifications
    // of at least ExtendedFilter and
    // CompositesIndex) and maybe we should, but that means a query like:
    //     SELECT * FROM foo WHERE static_column = 'bar'
    // would pull the full partition every time the static column of partition is 'bar', which
    // sounds like offering a
    // fair potential for foot-shooting, so I prefer leaving that to a follow up ticket once we have
    // identified cases where
    // such indexing is actually useful.
    if (cd.isStatic())
      throw new InvalidRequestException("Secondary indexes are not allowed on static columns");

    if (cd.kind == ColumnDefinition.Kind.PARTITION_KEY && cd.isOnAllComponents())
      throw new InvalidRequestException(
          String.format("Cannot create secondary index on partition key column %s", target.column));
  }