Ejemplo n.º 1
0
  public ColumnFamily updateForKey(
      ByteBuffer key, ColumnNameBuilder builder, UpdateParameters params)
      throws InvalidRequestException {
    CFDefinition cfDef = cfm.getCfDef();
    ColumnFamily cf = UnsortedColumns.factory.create(cfm);

    // Inserting the CQL row marker (see #4361)
    // We always need to insert a marker, because of the following situation:
    //   CREATE TABLE t ( k int PRIMARY KEY, c text );
    //   INSERT INTO t(k, c) VALUES (1, 1)
    //   DELETE c FROM t WHERE k = 1;
    //   SELECT * FROM t;
    // The last query should return one row (but with c == null). Adding
    // the marker with the insert make sure the semantic is correct (while making sure a
    // 'DELETE FROM t WHERE k = 1' does remove the row entirely)
    //
    // We never insert markers for Super CF as this would confuse the thrift side.
    if (cfDef.isComposite && !cfDef.isCompact && !cfm.isSuper()) {
      ByteBuffer name = builder.copy().add(ByteBufferUtil.EMPTY_BYTE_BUFFER).build();
      cf.addColumn(params.makeColumn(name, ByteBufferUtil.EMPTY_BYTE_BUFFER));
    }

    List<Operation> updates = getOperations();

    if (cfDef.isCompact) {
      if (builder.componentCount() == 0)
        throw new InvalidRequestException(
            String.format("Missing PRIMARY KEY part %s", cfDef.columns.values().iterator().next()));

      if (cfDef.value == null) {
        // compact + no compact value implies there is no column outside the PK. So no operation
        // could
        // have passed through validation
        assert updates.isEmpty();
        setToEmptyOperation.execute(key, cf, builder.copy(), params);
      } else {
        // compact means we don't have a row marker, so don't accept to set only the PK. See
        // CASSANDRA-5648.
        if (updates.isEmpty())
          throw new InvalidRequestException(
              String.format("Column %s is mandatory for this COMPACT STORAGE table", cfDef.value));

        for (Operation update : updates) update.execute(key, cf, builder.copy(), params);
      }
    } else {
      for (Operation update : updates) update.execute(key, cf, builder.copy(), params);
    }

    return cf;
  }
Ejemplo n.º 2
0
    protected ModificationStatement prepareInternal(
        CFDefinition cfDef, VariableSpecifications boundNames, Attributes attrs)
        throws InvalidRequestException {
      UpdateStatement stmt = new UpdateStatement(boundNames.size(), cfDef.cfm, attrs);

      // Created from an INSERT
      if (stmt.isCounter())
        throw new InvalidRequestException(
            "INSERT statement are not allowed on counter tables, use UPDATE instead");
      if (columnNames.size() != columnValues.size())
        throw new InvalidRequestException("Unmatched column names/values");
      if (columnNames.isEmpty()) throw new InvalidRequestException("No columns provided to INSERT");

      for (int i = 0; i < columnNames.size(); i++) {
        CFDefinition.Name name = cfDef.get(columnNames.get(i));
        if (name == null)
          throw new InvalidRequestException(
              String.format("Unknown identifier %s", columnNames.get(i)));

        for (int j = 0; j < i; j++)
          if (name.name.equals(columnNames.get(j)))
            throw new InvalidRequestException(
                String.format("Multiple definitions found for column %s", name));

        Term.Raw value = columnValues.get(i);

        switch (name.kind) {
          case KEY_ALIAS:
          case COLUMN_ALIAS:
            Term t = value.prepare(name);
            t.collectMarkerSpecification(boundNames);
            stmt.addKeyValue(name.name, t);
            break;
          case VALUE_ALIAS:
          case COLUMN_METADATA:
            Operation operation = new Operation.SetValue(value).prepare(name);
            operation.collectMarkerSpecification(boundNames);
            stmt.addOperation(operation);
            break;
        }
      }
      return stmt;
    }
    /** 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);
    }
  public RowMutation mutationForKey(
      CFDefinition cfDef,
      ByteBuffer key,
      ColumnNameBuilder builder,
      boolean isRange,
      UpdateParameters params,
      ColumnGroupMap group)
      throws InvalidRequestException {
    QueryProcessor.validateKey(key);
    RowMutation rm = new RowMutation(cfDef.cfm.ksName, key);
    ColumnFamily cf = rm.addOrGet(columnFamily());

    if (columns.isEmpty() && builder.componentCount() == 0) {
      // No columns, delete the row
      cf.delete(new DeletionInfo(params.timestamp, params.localDeletionTime));
    } else {
      if (isRange) {
        ByteBuffer start = builder.copy().build();
        ByteBuffer end = builder.buildAsEndOfRange();
        QueryProcessor.validateColumnName(start); // If start is good, end is too
        cf.addAtom(params.makeRangeTombstone(start, end));
      } else {
        // Delete specific columns
        if (cfDef.isCompact) {
          ByteBuffer columnName = builder.build();
          QueryProcessor.validateColumnName(columnName);
          cf.addColumn(params.makeTombstone(columnName));
        } else {
          Iterator<Pair<CFDefinition.Name, Term>> iter = toRemove.iterator();
          while (iter.hasNext()) {
            Pair<CFDefinition.Name, Term> p = iter.next();
            CFDefinition.Name column = p.left;

            if (column.type.isCollection()) {
              CollectionType validator = (CollectionType) column.type;
              Term keySelected = p.right;

              if (keySelected == null) {
                // Delete the whole collection
                ByteBuffer start = builder.copy().add(column.name.key).build();
                QueryProcessor.validateColumnName(start);
                ColumnNameBuilder b = iter.hasNext() ? builder.copy() : builder;
                ByteBuffer end = b.add(column.name.key).buildAsEndOfRange();
                cf.addAtom(params.makeRangeTombstone(start, end));
              } else {
                builder.add(column.name.key);
                List<Term> args = Collections.singletonList(keySelected);

                Operation op;
                switch (validator.kind) {
                  case LIST:
                    op = ListOperation.DiscardKey(args);
                    break;
                  case SET:
                    op = SetOperation.Discard(args);
                    break;
                  case MAP:
                    op = MapOperation.DiscardKey(keySelected);
                    break;
                  default:
                    throw new InvalidRequestException("Unknown collection type: " + validator.kind);
                }

                op.execute(
                    cf,
                    builder,
                    validator,
                    params,
                    group == null ? null : group.getCollection(column.name.key));
              }
            } else {
              ColumnNameBuilder b = iter.hasNext() ? builder.copy() : builder;
              ByteBuffer columnName = b.add(column.name.key).build();
              QueryProcessor.validateColumnName(columnName);
              cf.addColumn(params.makeTombstone(columnName));
            }
          }
        }
      }
    }

    return rm;
  }