org.apache.cassandra.cql3.restrictions.TokenFilter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-all Show documentation
Show all versions of cassandra-all Show documentation
The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.
/*
* 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.restrictions;
import java.nio.ByteBuffer;
import java.util.*;
import com.google.common.collect.BoundType;
import com.google.common.collect.ImmutableRangeSet;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.functions.Function;
import org.apache.cassandra.cql3.statements.Bound;
import org.apache.cassandra.db.filter.RowFilter;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.index.SecondaryIndexManager;
import static org.apache.cassandra.cql3.statements.Bound.END;
import static org.apache.cassandra.cql3.statements.Bound.START;
/**
* Restriction
decorator used to merge non-token restriction and token restriction on partition keys.
*
* If all partition key columns have non-token restrictions and do not need filtering, they take precedence
* when calculating bounds, incusiveness etc (see CASSANDRA-12149).
*/
final class TokenFilter implements PartitionKeyRestrictions
{
/**
* The decorated restriction
*/
private final PartitionKeyRestrictions restrictions;
/**
* The restriction on the token
*/
private final TokenRestriction tokenRestriction;
/**
* Partitioner to manage tokens, extracted from tokenRestriction metadata.
*/
private final IPartitioner partitioner;
public boolean hasIN()
{
return isOnToken() ? false : restrictions.hasIN();
}
public boolean hasContains()
{
return isOnToken() ? false : restrictions.hasContains();
}
public boolean hasOnlyEqualityRestrictions()
{
return isOnToken() ? false : restrictions.hasOnlyEqualityRestrictions();
}
@Override
public Set getRestrictions(ColumnDefinition columnDef)
{
Set set = new HashSet<>();
set.addAll(restrictions.getRestrictions(columnDef));
set.addAll(tokenRestriction.getRestrictions(columnDef));
return set;
}
@Override
public boolean isOnToken()
{
// if all partition key columns have non-token restrictions and do not need filtering,
// we can simply use the token range to filter those restrictions and then ignore the token range
return needFiltering(tokenRestriction.metadata) || restrictions.size() < tokenRestriction.size();
}
public TokenFilter(PartitionKeyRestrictions restrictions, TokenRestriction tokenRestriction)
{
this.restrictions = restrictions;
this.tokenRestriction = tokenRestriction;
this.partitioner = tokenRestriction.metadata.partitioner;
}
@Override
public List values(QueryOptions options) throws InvalidRequestException
{
return filter(restrictions.values(options), options);
}
@Override
public PartitionKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
{
if (restriction.isOnToken())
return new TokenFilter(restrictions, (TokenRestriction) tokenRestriction.mergeWith(restriction));
return new TokenFilter(restrictions.mergeWith(restriction), tokenRestriction);
}
@Override
public boolean isInclusive(Bound bound)
{
return isOnToken() ? tokenRestriction.isInclusive(bound) : restrictions.isInclusive(bound);
}
@Override
public boolean hasBound(Bound bound)
{
return isOnToken() ? tokenRestriction.hasBound(bound) : restrictions.hasBound(bound);
}
@Override
public List bounds(Bound bound, QueryOptions options) throws InvalidRequestException
{
return isOnToken() ? tokenRestriction.bounds(bound, options) : restrictions.bounds(bound, options);
}
/**
* Filter the values returned by the restriction.
*
* @param values the values returned by the decorated restriction
* @param options the query options
* @return the values matching the token restriction
* @throws InvalidRequestException if the request is invalid
*/
private List filter(List values, QueryOptions options) throws InvalidRequestException
{
RangeSet rangeSet = tokenRestriction.hasSlice() ? toRangeSet(tokenRestriction, options)
: toRangeSet(tokenRestriction.values(options));
return filterWithRangeSet(rangeSet, values);
}
/**
* Filter out the values for which the tokens are not included within the specified range.
*
* @param tokens the tokens range
* @param values the restricted values
* @return the values for which the tokens are not included within the specified range.
*/
private List filterWithRangeSet(RangeSet tokens, List values)
{
List remaining = new ArrayList<>();
for (ByteBuffer value : values)
{
Token token = partitioner.getToken(value);
if (!tokens.contains(token))
continue;
remaining.add(value);
}
return remaining;
}
/**
* Converts the specified list into a range set.
*
* @param buffers the token restriction values
* @return the range set corresponding to the specified list
*/
private RangeSet toRangeSet(List buffers)
{
ImmutableRangeSet.Builder builder = ImmutableRangeSet.builder();
for (ByteBuffer buffer : buffers)
builder.add(Range.singleton(deserializeToken(buffer)));
return builder.build();
}
/**
* Converts the specified slice into a range set.
*
* @param slice the slice to convert
* @param options the query option
* @return the range set corresponding to the specified slice
* @throws InvalidRequestException if the request is invalid
*/
private RangeSet toRangeSet(TokenRestriction slice, QueryOptions options) throws InvalidRequestException
{
if (slice.hasBound(START))
{
Token start = deserializeToken(slice.bounds(START, options).get(0));
BoundType startBoundType = toBoundType(slice.isInclusive(START));
if (slice.hasBound(END))
{
BoundType endBoundType = toBoundType(slice.isInclusive(END));
Token end = deserializeToken(slice.bounds(END, options).get(0));
if (start.equals(end) && (BoundType.OPEN == startBoundType || BoundType.OPEN == endBoundType))
return ImmutableRangeSet.of();
if (start.compareTo(end) <= 0)
return ImmutableRangeSet.of(Range.range(start,
startBoundType,
end,
endBoundType));
return ImmutableRangeSet. builder()
.add(Range.upTo(end, endBoundType))
.add(Range.downTo(start, startBoundType))
.build();
}
return ImmutableRangeSet.of(Range.downTo(start,
startBoundType));
}
Token end = deserializeToken(slice.bounds(END, options).get(0));
return ImmutableRangeSet.of(Range.upTo(end, toBoundType(slice.isInclusive(END))));
}
/**
* Deserializes the token corresponding to the specified buffer.
*
* @param buffer the buffer
* @return the token corresponding to the specified buffer
*/
private Token deserializeToken(ByteBuffer buffer)
{
return partitioner.getTokenFactory().fromByteArray(buffer);
}
private static BoundType toBoundType(boolean inclusive)
{
return inclusive ? BoundType.CLOSED : BoundType.OPEN;
}
@Override
public ColumnDefinition getFirstColumn()
{
return restrictions.getFirstColumn();
}
@Override
public ColumnDefinition getLastColumn()
{
return restrictions.getLastColumn();
}
@Override
public List getColumnDefs()
{
return restrictions.getColumnDefs();
}
@Override
public void addFunctionsTo(List functions)
{
restrictions.addFunctionsTo(functions);
}
@Override
public boolean hasSupportingIndex(SecondaryIndexManager indexManager)
{
return restrictions.hasSupportingIndex(indexManager);
}
@Override
public void addRowFilterTo(RowFilter filter, SecondaryIndexManager indexManager, QueryOptions options)
{
restrictions.addRowFilterTo(filter, indexManager, options);
}
@Override
public boolean isEmpty()
{
return restrictions.isEmpty();
}
@Override
public int size()
{
return restrictions.size();
}
@Override
public boolean needFiltering(CFMetaData cfm)
{
return restrictions.needFiltering(cfm);
}
@Override
public boolean hasUnrestrictedPartitionKeyComponents(CFMetaData cfm)
{
return restrictions.hasUnrestrictedPartitionKeyComponents(cfm);
}
@Override
public boolean hasSlice()
{
return restrictions.hasSlice();
}
}