/*
* 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.cql3;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import org.jboss.netty.buffer.ChannelBuffer;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.service.pager.PagingState;
import org.apache.cassandra.transport.CBCodec;
import org.apache.cassandra.transport.CBUtil;
/**
* Options for a query.
*/
public class QueryOptions
{
public static final QueryOptions DEFAULT = new QueryOptions(ConsistencyLevel.ONE, Collections.<ByteBuffer>emptyList());
public static final CBCodec<QueryOptions> codec = new Codec();
private final ConsistencyLevel consistency;
private final List<ByteBuffer> values;
private final boolean skipMetadata;
private final SpecificOptions options;
public QueryOptions(ConsistencyLevel consistency, List<ByteBuffer> values)
{
this(consistency, values, false, SpecificOptions.DEFAULT);
}
public QueryOptions(ConsistencyLevel consistency,
List<ByteBuffer> values,
boolean skipMetadata,
int pageSize,
PagingState pagingState,
ConsistencyLevel serialConsistency)
{
this(consistency, values, skipMetadata, new SpecificOptions(pageSize, pagingState, serialConsistency));
}
private QueryOptions(ConsistencyLevel consistency, List<ByteBuffer> values, boolean skipMetadata, SpecificOptions options)
{
this.consistency = consistency;
this.values = values;
this.skipMetadata = skipMetadata;
this.options = options;
}
public ConsistencyLevel getConsistency()
{
return consistency;
}
public List<ByteBuffer> getValues()
{
return values;
}
public boolean skipMetadata()
{
return skipMetadata;
}
/**
* The pageSize for this query. Will be <= 0 if not relevant for the query.
*/
public int getPageSize()
{
return options.pageSize;
}
/**
* The paging state for this query, or null if not relevant.
*/
public PagingState getPagingState()
{
return options.state;
}
/**
* Serial consistency for conditional updates.
*/
public ConsistencyLevel getSerialConsistency()
{
return options.serialConsistency;
}
// Options that are likely to not be present in most queries
private static class SpecificOptions
{
private static final SpecificOptions DEFAULT = new SpecificOptions(-1, null, null);
private final int pageSize;
private final PagingState state;
private final ConsistencyLevel serialConsistency;
private SpecificOptions(int pageSize, PagingState state, ConsistencyLevel serialConsistency)
{
this.pageSize = pageSize;
this.state = state;
this.serialConsistency = serialConsistency == null ? ConsistencyLevel.SERIAL : serialConsistency;
}
}
private static class Codec implements CBCodec<QueryOptions>
{
private static enum Flag
{
// The order of that enum matters!!
VALUES,
SKIP_METADATA,
PAGE_SIZE,
PAGING_STATE,
SERIAL_CONSISTENCY;
public static EnumSet<Flag> deserialize(int flags)
{
EnumSet<Flag> set = EnumSet.noneOf(Flag.class);
Flag[] values = Flag.values();
for (int n = 0; n < values.length; n++)
{
if ((flags & (1 << n)) != 0)
set.add(values[n]);
}
return set;
}
public static int serialize(EnumSet<Flag> flags)
{
int i = 0;
for (Flag flag : flags)
i |= 1 << flag.ordinal();
return i;
}
}
public QueryOptions decode(ChannelBuffer body, int version)
{
assert version >= 2;
ConsistencyLevel consistency = CBUtil.readConsistencyLevel(body);
EnumSet<Flag> flags = Flag.deserialize((int)body.readByte());
List<ByteBuffer> values = flags.contains(Flag.VALUES)
? CBUtil.readValueList(body)
: Collections.<ByteBuffer>emptyList();
boolean skipMetadata = flags.contains(Flag.SKIP_METADATA);
flags.remove(Flag.VALUES);
flags.remove(Flag.SKIP_METADATA);
SpecificOptions options = SpecificOptions.DEFAULT;
if (!flags.isEmpty())
{
int pageSize = flags.contains(Flag.PAGE_SIZE) ? body.readInt() : -1;
PagingState pagingState = flags.contains(Flag.PAGING_STATE) ? PagingState.deserialize(CBUtil.readValue(body)) : null;
ConsistencyLevel serialConsistency = flags.contains(Flag.SERIAL_CONSISTENCY) ? CBUtil.readConsistencyLevel(body) : ConsistencyLevel.SERIAL;
options = new SpecificOptions(pageSize, pagingState, serialConsistency);
}
return new QueryOptions(consistency, values, skipMetadata, options);
}
public void encode(QueryOptions options, ChannelBuffer dest, int version)
{
assert version >= 2;
CBUtil.writeConsistencyLevel(options.getConsistency(), dest);
EnumSet<Flag> flags = gatherFlags(options);
dest.writeByte((byte)Flag.serialize(flags));
if (flags.contains(Flag.VALUES))
CBUtil.writeValueList(options.getValues(), dest);
if (flags.contains(Flag.PAGE_SIZE))
dest.writeInt(options.getPageSize());
if (flags.contains(Flag.PAGING_STATE))
CBUtil.writeValue(options.getPagingState().serialize(), dest);
if (flags.contains(Flag.SERIAL_CONSISTENCY))
CBUtil.writeConsistencyLevel(options.getSerialConsistency(), dest);
}
public int encodedSize(QueryOptions options, int version)
{
int size = 0;
size += CBUtil.sizeOfConsistencyLevel(options.getConsistency());
EnumSet<Flag> flags = gatherFlags(options);
size += 1;
if (flags.contains(Flag.VALUES))
size += CBUtil.sizeOfValueList(options.getValues());
if (flags.contains(Flag.PAGE_SIZE))
size += 4;
if (flags.contains(Flag.PAGING_STATE))
size += CBUtil.sizeOfValue(options.getPagingState().serialize());
if (flags.contains(Flag.SERIAL_CONSISTENCY))
size += CBUtil.sizeOfConsistencyLevel(options.getSerialConsistency());
return size;
}
private EnumSet<Flag> gatherFlags(QueryOptions options)
{
EnumSet<Flag> flags = EnumSet.noneOf(Flag.class);
if (options.getValues().size() > 0)
flags.add(Flag.VALUES);
if (options.skipMetadata)
flags.add(Flag.SKIP_METADATA);
if (options.getPageSize() >= 0)
flags.add(Flag.PAGE_SIZE);
if (options.getPagingState() != null)
flags.add(Flag.PAGING_STATE);
if (options.getSerialConsistency() != ConsistencyLevel.SERIAL)
flags.add(Flag.SERIAL_CONSISTENCY);
return flags;
}
}
}