blob: 9aeea69842c2625f56781e09a8e181ffc91a0506 [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.cql3.restrictions;
import java.util.*;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.functions.Function;
import org.apache.cassandra.cql3.restrictions.SingleColumnRestriction.ContainsRestriction;
import org.apache.cassandra.db.filter.RowFilter;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.index.SecondaryIndexManager;
/**
* Sets of column restrictions.
*
* <p>This class is immutable in order to be use within {@link PrimaryKeyRestrictionSet} which as
* an implementation of {@link Restriction} need to be immutable.
*/
final class RestrictionSet implements Restrictions, Iterable<Restriction>
{
/**
* The comparator used to sort the <code>Restriction</code>s.
*/
private static final Comparator<ColumnDefinition> COLUMN_DEFINITION_COMPARATOR = new Comparator<ColumnDefinition>()
{
@Override
public int compare(ColumnDefinition column, ColumnDefinition otherColumn)
{
int value = Integer.compare(column.position(), otherColumn.position());
return value != 0 ? value : column.name.bytes.compareTo(otherColumn.name.bytes);
}
};
/**
* The restrictions per column.
*/
protected final TreeMap<ColumnDefinition, Restriction> restrictions;
public RestrictionSet()
{
this(new TreeMap<ColumnDefinition, Restriction>(COLUMN_DEFINITION_COMPARATOR));
}
private RestrictionSet(TreeMap<ColumnDefinition, Restriction> restrictions)
{
this.restrictions = restrictions;
}
@Override
public final void addRowFilterTo(RowFilter filter, SecondaryIndexManager indexManager, QueryOptions options) throws InvalidRequestException
{
for (Restriction restriction : restrictions.values())
restriction.addRowFilterTo(filter, indexManager, options);
}
@Override
public final List<ColumnDefinition> getColumnDefs()
{
return new ArrayList<>(restrictions.keySet());
}
@Override
public void addFunctionsTo(List<Function> functions)
{
Restriction previous = null;
for (Restriction restriction : restrictions.values())
{
// For muti-column restriction, we can have multiple time the same restriction.
if (!restriction.equals(previous))
{
previous = restriction;
restriction.addFunctionsTo(functions);
}
}
}
@Override
public final boolean isEmpty()
{
return getColumnDefs().isEmpty();
}
@Override
public final int size()
{
return getColumnDefs().size();
}
/**
* Adds the specified restriction to this set of restrictions.
*
* @param restriction the restriction to add
* @return the new set of restrictions
* @throws InvalidRequestException if the new restriction cannot be added
*/
public RestrictionSet addRestriction(Restriction restriction) throws InvalidRequestException
{
// RestrictionSet is immutable so we need to clone the restrictions map.
TreeMap<ColumnDefinition, Restriction> newRestrictions = new TreeMap<>(this.restrictions);
return new RestrictionSet(mergeRestrictions(newRestrictions, restriction));
}
private TreeMap<ColumnDefinition, Restriction> mergeRestrictions(TreeMap<ColumnDefinition, Restriction> restrictions,
Restriction restriction)
throws InvalidRequestException
{
Collection<ColumnDefinition> columnDefs = restriction.getColumnDefs();
Set<Restriction> existingRestrictions = getRestrictions(columnDefs);
if (existingRestrictions.isEmpty())
{
for (ColumnDefinition columnDef : columnDefs)
restrictions.put(columnDef, restriction);
}
else
{
for (Restriction existing : existingRestrictions)
{
Restriction newRestriction = mergeRestrictions(existing, restriction);
for (ColumnDefinition columnDef : columnDefs)
restrictions.put(columnDef, newRestriction);
}
}
return restrictions;
}
/**
* Returns all the restrictions applied to the specified columns.
*
* @param columnDefs the column definitions
* @return all the restrictions applied to the specified columns
*/
private Set<Restriction> getRestrictions(Collection<ColumnDefinition> columnDefs)
{
Set<Restriction> set = new HashSet<>();
for (ColumnDefinition columnDef : columnDefs)
{
Restriction existing = restrictions.get(columnDef);
if (existing != null)
set.add(existing);
}
return set;
}
@Override
public final boolean hasSupportingIndex(SecondaryIndexManager indexManager)
{
for (Restriction restriction : restrictions.values())
{
if (restriction.hasSupportingIndex(indexManager))
return true;
}
return false;
}
/**
* Returns the column after the specified one.
*
* @param columnDef the column for which the next one need to be found
* @return the column after the specified one.
*/
ColumnDefinition nextColumn(ColumnDefinition columnDef)
{
return restrictions.tailMap(columnDef, false).firstKey();
}
/**
* Returns the definition of the first column.
*
* @return the definition of the first column.
*/
ColumnDefinition firstColumn()
{
return isEmpty() ? null : this.restrictions.firstKey();
}
/**
* Returns the definition of the last column.
*
* @return the definition of the last column.
*/
ColumnDefinition lastColumn()
{
return isEmpty() ? null : this.restrictions.lastKey();
}
/**
* Returns the last restriction.
*
* @return the last restriction.
*/
Restriction lastRestriction()
{
return isEmpty() ? null : this.restrictions.lastEntry().getValue();
}
/**
* Merges the two specified restrictions.
*
* @param restriction the first restriction
* @param otherRestriction the second restriction
* @return the merged restriction
* @throws InvalidRequestException if the two restrictions cannot be merged
*/
private static Restriction mergeRestrictions(Restriction restriction,
Restriction otherRestriction) throws InvalidRequestException
{
return restriction == null ? otherRestriction
: restriction.mergeWith(otherRestriction);
}
/**
* Checks if the restrictions contains multiple contains, contains key, or map[key] = value.
*
* @return <code>true</code> if the restrictions contains multiple contains, contains key, or ,
* map[key] = value; <code>false</code> otherwise
*/
public final boolean hasMultipleContains()
{
int numberOfContains = 0;
for (Restriction restriction : restrictions.values())
{
if (restriction.isContains())
{
ContainsRestriction contains = (ContainsRestriction) restriction;
numberOfContains += (contains.numberOfValues() + contains.numberOfKeys() + contains.numberOfEntries());
}
}
return numberOfContains > 1;
}
@Override
public Iterator<Restriction> iterator()
{
return new LinkedHashSet<>(restrictions.values()).iterator();
}
}