blob: c4b4c750737fecfcd8513b29abdbedb502a0a193 [file] [log] [blame]
/*
* 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.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.db.marshal.SetType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.rows.*;
import org.apache.cassandra.db.context.CounterContext;
import org.apache.cassandra.db.partitions.*;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.ListType;
import org.apache.cassandra.db.marshal.MapType;
import org.apache.cassandra.utils.*;
/**
* Convenience object to create single row updates.
*
* This is meant for system table update, when performance is not of the utmost importance.
*/
public class RowUpdateBuilder
{
private final PartitionUpdate update;
private final long timestamp;
private final int ttl;
private final int localDeletionTime;
private final DeletionTime deletionTime;
private final Mutation mutation;
private Row.Builder regularBuilder;
private Row.Builder staticBuilder;
private boolean useRowMarker = true;
private RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl, int localDeletionTime, Mutation mutation)
{
this.update = update;
this.timestamp = timestamp;
this.ttl = ttl;
this.localDeletionTime = localDeletionTime;
this.deletionTime = new DeletionTime(timestamp, localDeletionTime);
// 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)
this.mutation = mutation == null ? new Mutation(update.metadata().ksName, update.partitionKey()).add(update) : mutation;
}
private RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl, Mutation mutation)
{
this(update, timestamp, ttl, FBUtilities.nowInSeconds(), mutation);
}
private void startRow(Clustering clustering)
{
assert staticBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
assert regularBuilder == null : "Cannot add the clustering twice to the same row";
regularBuilder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
regularBuilder.newRow(clustering);
// If a CQL table, add the "row marker"
if (update.metadata().isCQLTable() && useRowMarker)
regularBuilder.addPrimaryKeyLivenessInfo(LivenessInfo.create(update.metadata(), timestamp, ttl, localDeletionTime));
}
private Row.Builder builder()
{
assert staticBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
if (regularBuilder == null)
{
// we don't force people to call clustering() if the table has no clustering, so call it ourselves
assert update.metadata().comparator.size() == 0 : "Missing call to clustering()";
startRow(Clustering.EMPTY);
}
return regularBuilder;
}
private Row.Builder staticBuilder()
{
assert regularBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
if (staticBuilder == null)
{
staticBuilder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
staticBuilder.newRow(Clustering.STATIC_CLUSTERING);
}
return staticBuilder;
}
private Row.Builder builder(ColumnDefinition c)
{
return c.isStatic() ? staticBuilder() : builder();
}
public RowUpdateBuilder(CFMetaData metadata, long timestamp, Object partitionKey)
{
this(metadata, FBUtilities.nowInSeconds(), timestamp, partitionKey);
}
public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, Object partitionKey)
{
this(metadata, localDeletionTime, timestamp, metadata.params.defaultTimeToLive, partitionKey);
}
public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Object partitionKey)
{
this(metadata, FBUtilities.nowInSeconds(), timestamp, ttl, partitionKey);
}
public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, int ttl, Object partitionKey)
{
this(new PartitionUpdate(metadata, makeKey(metadata, partitionKey), metadata.partitionColumns(), 1), timestamp, ttl, localDeletionTime, null);
}
public RowUpdateBuilder(CFMetaData metadata, long timestamp, Mutation mutation)
{
this(metadata, timestamp, LivenessInfo.NO_TTL, mutation);
}
public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Mutation mutation)
{
this(getOrAdd(metadata, mutation), timestamp, ttl, mutation);
}
public RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl)
{
this(update, timestamp, ttl, null);
}
// This must be called before any addition or deletion if used.
public RowUpdateBuilder noRowMarker()
{
this.useRowMarker = false;
return this;
}
public RowUpdateBuilder clustering(Object... clusteringValues)
{
assert clusteringValues.length == update.metadata().comparator.size()
: "Invalid clustering values length. Expected: " + update.metadata().comparator.size() + " got: " + clusteringValues.length;
startRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
return this;
}
public Mutation build()
{
Row.Builder builder = regularBuilder == null ? staticBuilder : regularBuilder;
if (builder != null)
update.add(builder.build());
return mutation;
}
public PartitionUpdate buildUpdate()
{
build();
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(CFMetaData metadata, long timestamp, Mutation mutation, Object... clusteringValues)
{
deleteRow(getOrAdd(metadata, mutation), timestamp, FBUtilities.nowInSeconds(), clusteringValues);
return mutation;
}
public static Mutation deleteRow(CFMetaData metadata, long timestamp, Object key, Object... clusteringValues)
{
return deleteRowAt(metadata, timestamp, FBUtilities.nowInSeconds(), key, clusteringValues);
}
public static Mutation deleteRowAt(CFMetaData metadata, long timestamp, int localDeletionTime, Object key, Object... clusteringValues)
{
PartitionUpdate update = new PartitionUpdate(metadata, makeKey(metadata, key), metadata.partitionColumns(), 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().ksName, update.partitionKey()).add(update);
}
private static DecoratedKey makeKey(CFMetaData metadata, Object... partitionKey)
{
if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
return (DecoratedKey)partitionKey[0];
ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
return metadata.decorateKey(key);
}
private static PartitionUpdate getOrAdd(CFMetaData metadata, Mutation mutation)
{
PartitionUpdate upd = mutation.get(metadata);
if (upd == null)
{
upd = new PartitionUpdate(metadata, mutation.key(), metadata.partitionColumns(), 1);
mutation.add(upd);
}
return upd;
}
public RowUpdateBuilder resetCollection(String columnName)
{
ColumnDefinition c = getDefinition(columnName);
assert c != null : "Cannot find column " + columnName;
assert c.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
assert c.type.isCollection() && c.type.isMultiCell();
builder(c).addComplexDeletion(c, new DeletionTime(timestamp - 1, localDeletionTime));
return this;
}
public RowUpdateBuilder addRangeTombstone(RangeTombstone rt)
{
update.add(rt);
return this;
}
public RowUpdateBuilder addRangeTombstone(Slice slice)
{
return addRangeTombstone(new RangeTombstone(slice, deletionTime));
}
public RowUpdateBuilder addRangeTombstone(Object start, Object end)
{
ClusteringComparator cmp = update.metadata().comparator;
Slice slice = Slice.make(cmp.make(start), cmp.make(end));
return addRangeTombstone(slice);
}
public RowUpdateBuilder add(String columnName, Object value)
{
ColumnDefinition c = getDefinition(columnName);
assert c != null : "Cannot find column " + columnName;
return add(c, value);
}
private Cell makeCell(ColumnDefinition c, ByteBuffer value, CellPath path)
{
return value == null
? BufferCell.tombstone(c, timestamp, localDeletionTime)
: (ttl == LivenessInfo.NO_TTL ? BufferCell.live(update.metadata(), c, timestamp, value, path) : BufferCell.expiring(c, timestamp, ttl, localDeletionTime, value, path));
}
public RowUpdateBuilder add(ColumnDefinition columnDefinition, Object value)
{
assert columnDefinition.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + columnDefinition + " since no clustering hasn't been provided";
builder(columnDefinition).addCell(makeCell(columnDefinition, bb(value, columnDefinition.type), null));
return this;
}
public RowUpdateBuilder delete(String columnName)
{
ColumnDefinition c = getDefinition(columnName);
assert c != null : "Cannot find column " + columnName;
return delete(c);
}
public RowUpdateBuilder delete(ColumnDefinition columnDefinition)
{
return add(columnDefinition, null);
}
private static ByteBuffer bb(Object value, AbstractType<?> type)
{
if (value == null)
return null;
if (value instanceof ByteBuffer)
return (ByteBuffer)value;
if (type.isCounter())
{
// See UpdateParameters.addCounter()
assert value instanceof Long : "Attempted to adjust Counter cell with non-long value.";
return CounterContext.instance().createGlobal(CounterId.getLocalId(), 1, (Long)value);
}
return ((AbstractType)type).decompose(value);
}
public RowUpdateBuilder map(String columnName, Map<?, ?> map)
{
resetCollection(columnName);
for (Map.Entry<?, ?> entry : map.entrySet())
addMapEntry(columnName, entry.getKey(), entry.getValue());
return this;
}
public RowUpdateBuilder set(String columnName, Set<?> set)
{
resetCollection(columnName);
for (Object element : set)
addSetEntry(columnName, element);
return this;
}
public RowUpdateBuilder frozenList(String columnName, List<?> list)
{
ColumnDefinition c = getDefinition(columnName);
assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
assert c.type instanceof ListType && !c.type.isMultiCell() : "Column " + c + " is not a frozen list";
builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(list), c.type), null));
return this;
}
public RowUpdateBuilder frozenSet(String columnName, Set<?> set)
{
ColumnDefinition c = getDefinition(columnName);
assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
assert c.type instanceof SetType && !c.type.isMultiCell() : "Column " + c + " is not a frozen set";
builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(set), c.type), null));
return this;
}
public RowUpdateBuilder frozenMap(String columnName, Map<?, ?> map)
{
ColumnDefinition c = getDefinition(columnName);
assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
assert c.type instanceof MapType && !c.type.isMultiCell() : "Column " + c + " is not a frozen map";
builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(map), c.type), null));
return this;
}
public RowUpdateBuilder addMapEntry(String columnName, Object key, Object value)
{
ColumnDefinition c = getDefinition(columnName);
assert c.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
assert c.type instanceof MapType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen map";
MapType mt = (MapType)c.type;
builder(c).addCell(makeCell(c, bb(value, mt.getValuesType()), CellPath.create(bb(key, mt.getKeysType()))));
return this;
}
public RowUpdateBuilder addListEntry(String columnName, Object value)
{
ColumnDefinition c = getDefinition(columnName);
assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
assert c.type instanceof ListType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen list";
ListType lt = (ListType)c.type;
builder(c).addCell(makeCell(c, bb(value, lt.getElementsType()), CellPath.create(ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes()))));
return this;
}
public RowUpdateBuilder addSetEntry(String columnName, Object value)
{
ColumnDefinition c = getDefinition(columnName);
assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
assert c.type instanceof SetType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen set";
SetType st = (SetType)c.type;
builder(c).addCell(makeCell(c, ByteBufferUtil.EMPTY_BYTE_BUFFER, CellPath.create(bb(value, st.getElementsType()))));
return this;
}
private ColumnDefinition getDefinition(String name)
{
return update.metadata().getColumnDefinitionForCQL(new ColumnIdentifier(name, true));
}
public UnfilteredRowIterator unfilteredIterator()
{
return update.unfilteredIterator();
}
}