Exemplo n.º 1
0
  public void announceMigration() throws RequestValidationException {
    CFMetaData meta = validateColumnFamily(keyspace(), columnFamily());
    CFMetaData cfm = meta.clone();

    CFDefinition cfDef = meta.getCfDef();
    CFDefinition.Name name = columnName == null ? null : cfDef.get(columnName);
    switch (oType) {
      case ADD:
        if (cfDef.isCompact)
          throw new InvalidRequestException("Cannot add new column to a compact CF");
        if (name != null) {
          switch (name.kind) {
            case KEY_ALIAS:
            case COLUMN_ALIAS:
              throw new InvalidRequestException(
                  String.format(
                      "Invalid column name %s because it conflicts with a PRIMARY KEY part",
                      columnName));
            case COLUMN_METADATA:
              throw new InvalidRequestException(
                  String.format(
                      "Invalid column name %s because it conflicts with an existing column",
                      columnName));
          }
        }

        AbstractType<?> type = validator.getType();
        if (type instanceof CollectionType) {
          if (!cfDef.isComposite)
            throw new InvalidRequestException(
                "Cannot use collection types with non-composite PRIMARY KEY");
          if (cfDef.cfm.isSuper())
            throw new InvalidRequestException(
                "Cannot use collection types with Super column family");

          Map<ByteBuffer, CollectionType> collections =
              cfDef.hasCollections
                  ? new HashMap<ByteBuffer, CollectionType>(cfDef.getCollectionType().defined)
                  : new HashMap<ByteBuffer, CollectionType>();

          collections.put(columnName.key, (CollectionType) type);
          ColumnToCollectionType newColType = ColumnToCollectionType.getInstance(collections);
          List<AbstractType<?>> ctypes =
              new ArrayList<AbstractType<?>>(((CompositeType) cfm.comparator).types);
          if (cfDef.hasCollections) ctypes.set(ctypes.size() - 1, newColType);
          else ctypes.add(newColType);
          cfm.comparator = CompositeType.getInstance(ctypes);
        }

        Integer componentIndex =
            cfDef.isComposite
                ? ((CompositeType) meta.comparator).types.size() - (cfDef.hasCollections ? 2 : 1)
                : null;
        cfm.addColumnDefinition(ColumnDefinition.regularDef(columnName.key, type, componentIndex));
        break;

      case ALTER:
        if (name == null)
          throw new InvalidRequestException(
              String.format("Column %s was not found in table %s", columnName, columnFamily()));

        switch (name.kind) {
          case KEY_ALIAS:
            AbstractType<?> newType = validator.getType();
            if (newType instanceof CounterColumnType)
              throw new InvalidRequestException(
                  String.format(
                      "counter type is not supported for PRIMARY KEY part %s", columnName));
            if (cfDef.hasCompositeKey) {
              List<AbstractType<?>> newTypes =
                  new ArrayList<AbstractType<?>>(((CompositeType) cfm.getKeyValidator()).types);
              newTypes.set(name.position, newType);
              cfm.keyValidator(CompositeType.getInstance(newTypes));
            } else {
              cfm.keyValidator(newType);
            }
            break;
          case COLUMN_ALIAS:
            assert cfDef.isComposite;
            List<AbstractType<?>> newTypes =
                new ArrayList<AbstractType<?>>(((CompositeType) cfm.comparator).types);
            newTypes.set(name.position, validator.getType());
            cfm.comparator = CompositeType.getInstance(newTypes);
            break;
          case VALUE_ALIAS:
            cfm.defaultValidator(validator.getType());
            break;
          case COLUMN_METADATA:
            ColumnDefinition column = cfm.getColumnDefinition(columnName.key);
            column.setValidator(validator.getType());
            break;
        }
        break;

      case DROP:
        if (cfDef.isCompact)
          throw new InvalidRequestException("Cannot drop columns from a compact CF");
        if (name == null)
          throw new InvalidRequestException(
              String.format("Column %s was not found in table %s", columnName, columnFamily()));

        switch (name.kind) {
          case KEY_ALIAS:
          case COLUMN_ALIAS:
            throw new InvalidRequestException(
                String.format("Cannot drop PRIMARY KEY part %s", columnName));
          case COLUMN_METADATA:
            ColumnDefinition toDelete = null;
            for (ColumnDefinition columnDef : cfm.regularColumns()) {
              if (columnDef.name.equals(columnName.key)) toDelete = columnDef;
            }
            assert toDelete != null;
            cfm.removeColumnDefinition(toDelete);
            break;
        }
        break;
      case OPTS:
        if (cfProps == null)
          throw new InvalidRequestException(
              String.format("ALTER COLUMNFAMILY WITH invoked, but no parameters found"));

        cfProps.validate();
        cfProps.applyToCFMetadata(cfm);
        break;
      case RENAME:
        for (Map.Entry<ColumnIdentifier, ColumnIdentifier> entry : renames.entrySet()) {
          ColumnIdentifier from = entry.getKey();
          ColumnIdentifier to = entry.getValue();
          cfm.renameColumn(from.key, from.toString(), to.key, to.toString());
        }
        break;
    }

    MigrationManager.announceColumnFamilyUpdate(cfm, false);
  }
    /** Transform this raw statement into a CreateTableStatement. */
    public ParsedStatement.Prepared prepare() throws RequestValidationException {
      // Column family name
      if (!columnFamily().matches("\\w+"))
        throw new InvalidRequestException(
            String.format(
                "\"%s\" is not a valid column family name (must be alphanumeric character only: [0-9A-Za-z]+)",
                columnFamily()));
      if (columnFamily().length() > Schema.NAME_LENGTH)
        throw new InvalidRequestException(
            String.format(
                "Column family names shouldn't be more than %s characters long (got \"%s\")",
                Schema.NAME_LENGTH, columnFamily()));

      for (Multiset.Entry<ColumnIdentifier> entry : definedNames.entrySet())
        if (entry.getCount() > 1)
          throw new InvalidRequestException(
              String.format("Multiple definition of identifier %s", entry.getElement()));

      properties.validate();
      CreateTableStatement stmt =
          new CreateTableStatement(cfName, properties, ifNotExists, staticColumns);

      Map<ByteBuffer, CollectionType> definedCollections = null;
      for (Map.Entry<ColumnIdentifier, CQL3Type> entry : definitions.entrySet()) {

        ColumnIdentifier id = entry.getKey();
        CQL3Type pt = entry.getValue();
        if (pt.isCollection()) {
          if (definedCollections == null)
            definedCollections = new HashMap<ByteBuffer, CollectionType>();
          definedCollections.put(id.key, (CollectionType) pt.getType());
        }
        stmt.columns.put(id, pt.getType()); // we'll remove what is not a column below
      }

      if (keyAliases.isEmpty())
        throw new InvalidRequestException("No PRIMARY KEY specifed (exactly one required)");
      else if (keyAliases.size() > 1)
        throw new InvalidRequestException("Multiple PRIMARY KEYs specifed (exactly one required)");

      List<ColumnIdentifier> kAliases = keyAliases.get(0);

      List<AbstractType<?>> keyTypes = new ArrayList<AbstractType<?>>(kAliases.size());
      for (ColumnIdentifier alias : kAliases) {
        stmt.keyAliases.add(alias.key);
        AbstractType<?> t = getTypeAndRemove(stmt.columns, alias);
        if (t instanceof CounterColumnType)
          throw new InvalidRequestException(
              String.format("counter type is not supported for PRIMARY KEY part %s", alias));
        if (staticColumns.contains(alias))
          throw new InvalidRequestException(
              String.format("Static column %s cannot be part of the PRIMARY KEY", alias));
        keyTypes.add(t);
      }
      stmt.keyValidator =
          keyTypes.size() == 1 ? keyTypes.get(0) : CompositeType.getInstance(keyTypes);

      // Dense means that no part of the comparator stores a CQL column name. This means
      // COMPACT STORAGE with at least one columnAliases (otherwise it's a thrift "static" CF).
      stmt.isDense = useCompactStorage && !columnAliases.isEmpty();

      // Handle column aliases
      if (columnAliases.isEmpty()) {
        if (useCompactStorage) {
          // There should remain some column definition since it is a non-composite "static" CF
          if (stmt.columns.isEmpty())
            throw new InvalidRequestException(
                "No definition found that is not part of the PRIMARY KEY");

          if (definedCollections != null)
            throw new InvalidRequestException(
                "Collection types are not supported with COMPACT STORAGE");

          stmt.comparator = CFDefinition.definitionType;
        } else {
          List<AbstractType<?>> types =
              new ArrayList<AbstractType<?>>(definedCollections == null ? 1 : 2);
          types.add(CFDefinition.definitionType);
          if (definedCollections != null)
            types.add(ColumnToCollectionType.getInstance(definedCollections));
          stmt.comparator = CompositeType.getInstance(types);
        }
      } else {
        // If we use compact storage and have only one alias, it is a
        // standard "dynamic" CF, otherwise it's a composite
        if (useCompactStorage && columnAliases.size() == 1) {
          if (definedCollections != null)
            throw new InvalidRequestException(
                "Collection types are not supported with COMPACT STORAGE");
          ColumnIdentifier alias = columnAliases.get(0);
          stmt.columnAliases.add(alias.key);
          stmt.comparator = getTypeAndRemove(stmt.columns, alias);
          if (stmt.comparator instanceof CounterColumnType)
            throw new InvalidRequestException(
                String.format("counter type is not supported for PRIMARY KEY part %s", alias));
          if (staticColumns.contains(alias))
            throw new InvalidRequestException(
                String.format("Static column %s cannot be part of the PRIMARY KEY", alias));
        } else {
          List<AbstractType<?>> types = new ArrayList<AbstractType<?>>(columnAliases.size() + 1);
          for (ColumnIdentifier t : columnAliases) {
            stmt.columnAliases.add(t.key);

            AbstractType<?> type = getTypeAndRemove(stmt.columns, t);
            if (type instanceof CounterColumnType)
              throw new InvalidRequestException(
                  String.format("counter type is not supported for PRIMARY KEY part %s", t));
            if (staticColumns.contains(t))
              throw new InvalidRequestException(
                  String.format("Static column %s cannot be part of the PRIMARY KEY", t));
            types.add(type);
          }

          if (useCompactStorage) {
            if (definedCollections != null)
              throw new InvalidRequestException(
                  "Collection types are not supported with COMPACT STORAGE");
          } else {
            // For sparse, we must add the last UTF8 component
            // and the collection type if there is one
            types.add(CFDefinition.definitionType);
            if (definedCollections != null)
              types.add(ColumnToCollectionType.getInstance(definedCollections));
          }

          if (types.isEmpty())
            throw new IllegalStateException("Nonsensical empty parameter list for CompositeType");
          stmt.comparator = CompositeType.getInstance(types);
        }
      }

      if (!staticColumns.isEmpty()) {
        // Only CQL3 tables can have static columns
        if (useCompactStorage)
          throw new InvalidRequestException(
              "Static columns are not supported in COMPACT STORAGE tables");
        // Static columns only make sense if we have at least one clustering column. Otherwise
        // everything is static anyway
        if (columnAliases.isEmpty())
          throw new InvalidRequestException(
              "Static columns are only useful (and thus allowed) if the table has at least one clustering column");
      }

      if (useCompactStorage && !stmt.columnAliases.isEmpty()) {
        if (stmt.columns.isEmpty()) {
          // The only value we'll insert will be the empty one, so the default validator don't
          // matter
          stmt.defaultValidator = BytesType.instance;
          // We need to distinguish between
          //   * I'm upgrading from thrift so the valueAlias is null
          //   * I've defined my table with only a PK (and the column value will be empty)
          // So, we use an empty valueAlias (rather than null) for the second case
          stmt.valueAlias = ByteBufferUtil.EMPTY_BYTE_BUFFER;
        } else {
          if (stmt.columns.size() > 1)
            throw new InvalidRequestException(
                String.format(
                    "COMPACT STORAGE with composite PRIMARY KEY allows no more than one column not part of the PRIMARY KEY (got: %s)",
                    StringUtils.join(stmt.columns.keySet(), ", ")));

          Map.Entry<ColumnIdentifier, AbstractType> lastEntry =
              stmt.columns.entrySet().iterator().next();
          stmt.defaultValidator = lastEntry.getValue();
          stmt.valueAlias = lastEntry.getKey().key;
          stmt.columns.remove(lastEntry.getKey());
        }
      } else {
        // For compact, we are in the "static" case, so we need at least one column defined. For
        // non-compact however, having
        // just the PK is fine since we have CQL3 row marker.
        if (useCompactStorage && stmt.columns.isEmpty())
          throw new InvalidRequestException(
              "COMPACT STORAGE with non-composite PRIMARY KEY require one column not part of the PRIMARY KEY, none given");

        // There is no way to insert/access a column that is not defined for non-compact storage, so
        // the actual validator don't matter much (except that we want to recognize counter CF as
        // limitation apply to them).
        stmt.defaultValidator =
            !stmt.columns.isEmpty()
                    && (stmt.columns.values().iterator().next() instanceof CounterColumnType)
                ? CounterColumnType.instance
                : BytesType.instance;
      }

      // If we give a clustering order, we must explicitly do so for all aliases and in the order of
      // the PK
      if (!definedOrdering.isEmpty()) {
        if (definedOrdering.size() > columnAliases.size())
          throw new InvalidRequestException(
              "Only clustering key columns can be defined in CLUSTERING ORDER directive");

        int i = 0;
        for (ColumnIdentifier id : definedOrdering.keySet()) {
          ColumnIdentifier c = columnAliases.get(i);
          if (!id.equals(c)) {
            if (definedOrdering.containsKey(c))
              throw new InvalidRequestException(
                  String.format(
                      "The order of columns in the CLUSTERING ORDER directive must be the one of the clustering key (%s must appear before %s)",
                      c, id));
            else
              throw new InvalidRequestException(
                  String.format("Missing CLUSTERING ORDER for column %s", c));
          }
          ++i;
        }
      }

      return new ParsedStatement.Prepared(stmt);
    }