/* * 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.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.Schema; import org.apache.cassandra.db.filter.IDiskAtomFilter; import org.apache.cassandra.db.filter.NamesQueryFilter; import org.apache.cassandra.db.filter.SliceQueryFilter; import org.apache.cassandra.db.marshal.CompositeType; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.service.IReadCommand; import org.apache.cassandra.service.RowDataResolver; import org.apache.cassandra.service.pager.Pageable; public abstract class ReadCommand implements IReadCommand, Pageable { public enum Type { GET_BY_NAMES((byte)1), GET_SLICES((byte)2); public final byte serializedValue; private Type(byte b) { this.serializedValue = b; } public static Type fromSerializedValue(byte b) { return b == 1 ? GET_BY_NAMES : GET_SLICES; } } public static final ReadCommandSerializer serializer = new ReadCommandSerializer(); public MessageOut<ReadCommand> createMessage() { return new MessageOut<ReadCommand>(MessagingService.Verb.READ, this, serializer); } public final String ksName; public final String cfName; public final ByteBuffer key; public final long timestamp; private boolean isDigestQuery = false; protected final Type commandType; protected ReadCommand(String ksName, ByteBuffer key, String cfName, long timestamp, Type cmdType) { this.ksName = ksName; this.key = key; this.cfName = cfName; this.timestamp = timestamp; this.commandType = cmdType; } public static ReadCommand create(String ksName, ByteBuffer key, String cfName, long timestamp, IDiskAtomFilter filter) { if (filter instanceof SliceQueryFilter) return new SliceFromReadCommand(ksName, key, cfName, timestamp, (SliceQueryFilter)filter); else return new SliceByNamesReadCommand(ksName, key, cfName, timestamp, (NamesQueryFilter)filter); } public boolean isDigestQuery() { return isDigestQuery; } public void setDigestQuery(boolean isDigestQuery) { this.isDigestQuery = isDigestQuery; } public String getColumnFamilyName() { return cfName; } public abstract ReadCommand copy(); public abstract Row getRow(Keyspace keyspace); public abstract IDiskAtomFilter filter(); public String getKeyspace() { return ksName; } // maybeGenerateRetryCommand is used to generate a retry for short reads public ReadCommand maybeGenerateRetryCommand(RowDataResolver resolver, Row row) { return null; } // maybeTrim removes columns from a response that is too long public void maybeTrim(Row row) { // noop } public long getTimeout() { return DatabaseDescriptor.getReadRpcTimeout(); } } class ReadCommandSerializer implements IVersionedSerializer<ReadCommand> { public void serialize(ReadCommand command, DataOutput out, int version) throws IOException { // For super columns, when talking to an older node, we need to translate the filter used. // That translation can change the filter type (names -> slice), and so change the command type. // Hence we need to detect that early on, before we've written the command type. ReadCommand newCommand = command; ByteBuffer superColumn = null; if (version < MessagingService.VERSION_20) { CFMetaData metadata = Schema.instance.getCFMetaData(command.ksName, command.cfName); if (metadata.cfType == ColumnFamilyType.Super) { SuperColumns.SCFilter scFilter = SuperColumns.filterToSC((CompositeType)metadata.comparator, command.filter()); newCommand = ReadCommand.create(command.ksName, command.key, command.cfName, command.timestamp, scFilter.updatedFilter); newCommand.setDigestQuery(command.isDigestQuery()); superColumn = scFilter.scName; } } out.writeByte(newCommand.commandType.serializedValue); switch (command.commandType) { case GET_BY_NAMES: SliceByNamesReadCommand.serializer.serialize(newCommand, superColumn, out, version); break; case GET_SLICES: SliceFromReadCommand.serializer.serialize(newCommand, superColumn, out, version); break; default: throw new AssertionError(); } } public ReadCommand deserialize(DataInput in, int version) throws IOException { ReadCommand.Type msgType = ReadCommand.Type.fromSerializedValue(in.readByte()); switch (msgType) { case GET_BY_NAMES: return SliceByNamesReadCommand.serializer.deserialize(in, version); case GET_SLICES: return SliceFromReadCommand.serializer.deserialize(in, version); default: throw new AssertionError(); } } public long serializedSize(ReadCommand command, int version) { ReadCommand newCommand = command; ByteBuffer superColumn = null; if (version < MessagingService.VERSION_20) { CFMetaData metadata = Schema.instance.getCFMetaData(command.ksName, command.cfName); if (metadata.cfType == ColumnFamilyType.Super) { SuperColumns.SCFilter scFilter = SuperColumns.filterToSC((CompositeType)metadata.comparator, command.filter()); newCommand = ReadCommand.create(command.ksName, command.key, command.cfName, command.timestamp, scFilter.updatedFilter); newCommand.setDigestQuery(command.isDigestQuery()); superColumn = scFilter.scName; } } switch (command.commandType) { case GET_BY_NAMES: return 1 + SliceByNamesReadCommand.serializer.serializedSize(newCommand, superColumn, version); case GET_SLICES: return 1 + SliceFromReadCommand.serializer.serializedSize(newCommand, superColumn, version); default: throw new AssertionError(); } } }