/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cassandra.db; import java.nio.ByteBuffer; import java.util.*; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.db.rows.*; import org.apache.cassandra.db.partitions.*; import org.apache.cassandra.utils.*; /** * Convenience object to create single row updates for tests. * * This is a thin wrapper over the builders in SimpleBuilders for historical reasons. * We could modify all the tests using this class to use the simple builders directly * instead, but there is a fair amount of use so the value of such effort is unclear. */ public class RowUpdateBuilder { private final PartitionUpdate.SimpleBuilder updateBuilder; private Row.SimpleBuilder rowBuilder; private boolean noRowMarker; private List<RangeTombstone> rts = new ArrayList<>(); private RowUpdateBuilder(PartitionUpdate.SimpleBuilder updateBuilder) { this.updateBuilder = updateBuilder; } public RowUpdateBuilder(TableMetadata metadata, long timestamp, Object partitionKey) { this(metadata, FBUtilities.nowInSeconds(), timestamp, partitionKey); } public RowUpdateBuilder(TableMetadata metadata, int localDeletionTime, long timestamp, Object partitionKey) { this(metadata, localDeletionTime, timestamp, metadata.params.defaultTimeToLive, partitionKey); } public RowUpdateBuilder(TableMetadata metadata, long timestamp, int ttl, Object partitionKey) { this(metadata, FBUtilities.nowInSeconds(), timestamp, ttl, partitionKey); } public RowUpdateBuilder(TableMetadata metadata, int localDeletionTime, long timestamp, int ttl, Object partitionKey) { this(PartitionUpdate.simpleBuilder(metadata, partitionKey)); this.updateBuilder.timestamp(timestamp); this.updateBuilder.ttl(ttl); this.updateBuilder.nowInSec(localDeletionTime); } private Row.SimpleBuilder rowBuilder() { // Normally, rowBuilder is created by the call to clustering(), but we allow skipping that call for an empty // clustering. if (rowBuilder == null) { rowBuilder = updateBuilder.row(); if (noRowMarker) rowBuilder.noPrimaryKeyLivenessInfo(); } return rowBuilder; } // This must be called before any addition or deletion if used. public RowUpdateBuilder noRowMarker() { this.noRowMarker = true; if (rowBuilder != null) rowBuilder.noPrimaryKeyLivenessInfo(); return this; } public RowUpdateBuilder clustering(Object... clusteringValues) { assert rowBuilder == null; rowBuilder = updateBuilder.row(clusteringValues); if (noRowMarker) rowBuilder.noPrimaryKeyLivenessInfo(); return this; } public Mutation build() { return new Mutation(buildUpdate()); } public PartitionUpdate buildUpdate() { PartitionUpdate update = updateBuilder.build(); for (RangeTombstone rt : rts) update.add(rt); return update; } private static void deleteRow(PartitionUpdate update, long timestamp, int localDeletionTime, Object... clusteringValues) { assert clusteringValues.length == update.metadata().comparator.size() || (clusteringValues.length == 0 && !update.columns().statics.isEmpty()); boolean isStatic = clusteringValues.length != update.metadata().comparator.size(); Row.Builder builder = BTreeRow.sortedBuilder(); if (isStatic) builder.newRow(Clustering.STATIC_CLUSTERING); else builder.newRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues)); builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, localDeletionTime))); update.add(builder.build()); } public static Mutation deleteRow(TableMetadata metadata, long timestamp, Object key, Object... clusteringValues) { return deleteRowAt(metadata, timestamp, FBUtilities.nowInSeconds(), key, clusteringValues); } public static Mutation deleteRowAt(TableMetadata metadata, long timestamp, int localDeletionTime, Object key, Object... clusteringValues) { PartitionUpdate update = new PartitionUpdate(metadata, makeKey(metadata, key), metadata.regularAndStaticColumns(), 0); deleteRow(update, timestamp, localDeletionTime, clusteringValues); // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap // underneath (this class if for convenience, not performance) return new Mutation(update.metadata().keyspace, update.partitionKey()).add(update); } private static DecoratedKey makeKey(TableMetadata metadata, Object... partitionKey) { if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey) return (DecoratedKey)partitionKey[0]; ByteBuffer key = metadata.partitionKeyAsClusteringComparator().make(partitionKey).serializeAsPartitionKey(); return metadata.partitioner.decorateKey(key); } public RowUpdateBuilder addRangeTombstone(RangeTombstone rt) { rts.add(rt); return this; } public RowUpdateBuilder addRangeTombstone(Object start, Object end) { updateBuilder.addRangeTombstone().start(start).end(end); return this; } public RowUpdateBuilder add(String columnName, Object value) { rowBuilder().add(columnName, value); return this; } public RowUpdateBuilder add(ColumnMetadata columnMetadata, Object value) { return add(columnMetadata.name.toString(), value); } public RowUpdateBuilder delete(String columnName) { rowBuilder().delete(columnName); return this; } public RowUpdateBuilder delete(ColumnMetadata columnMetadata) { return delete(columnMetadata.name.toString()); } }