Merge branch 'cassandra-2.0' into cassandra-2.1

This commit is contained in:
Tyler Hobbs 2015-03-05 12:29:26 -06:00
commit 9c7a601bbd
7 changed files with 1387 additions and 221 deletions

View File

@ -31,6 +31,8 @@
marked are in the live set (CASSANDRA-8689)
* cassandra-stress support for varint (CASSANDRA-8882)
Merged from 2.0:
* Fix regression in mixed single and multi-column relation support for
SELECT statements (CASSANDRA-8613)
* Add ability to limit number of native connections (CASSANDRA-8086)
* Add offline tool to relevel sstables (CASSANDRA-8301)
* Preserve stream ID for more protocol errors (CASSANDRA-8848)

View File

@ -59,7 +59,7 @@ public interface MultiColumnRestriction extends Restriction
*/
public static class InWithValues extends SingleColumnRestriction.InWithValues implements MultiColumnRestriction.IN
{
public InWithValues(List<Term> values)
public InWithValues(List<? extends Term> values)
{
super(values);
}

View File

@ -72,6 +72,6 @@ public interface Restriction
public Operator getIndexOperator(Bound b);
public void setBound(ColumnIdentifier name, Operator type, Term t) throws InvalidRequestException;
public void setBound(Operator type, Term t) throws InvalidRequestException;
}
}

View File

@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.statements;
import java.nio.ByteBuffer;
import java.util.*;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
@ -716,18 +717,38 @@ public class SelectStatement implements CQLStatement
assert !isColumnRange();
CBuilder builder = cfm.comparator.prefixBuilder();
Iterator<ColumnDefinition> idIter = cfm.clusteringColumns().iterator();
for (Restriction r : columnRestrictions)
Iterator<ColumnDefinition> columnIter = cfm.clusteringColumns().iterator();
while (columnIter.hasNext())
{
ColumnDefinition def = idIter.next();
ColumnDefinition def = columnIter.next();
Restriction r = columnRestrictions[def.position()];
assert r != null && !r.isSlice();
if (r.isEQ())
{
ByteBuffer val = r.values(options).get(0);
if (val == null)
throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
builder.add(val);
List<ByteBuffer> values = r.values(options);
if (r.isMultiColumn())
{
for (int i = 0, m = values.size(); i < m; i++)
{
ByteBuffer val = values.get(i);
if (i != 0)
columnIter.next();
if (val == null)
throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
builder.add(val);
}
}
else
{
ByteBuffer val = r.values(options).get(0);
if (val == null)
throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
builder.add(val);
}
}
else
{
@ -752,22 +773,21 @@ public class SelectStatement implements CQLStatement
}
return columns;
}
else
{
// we have a multi-column IN restriction
List<List<ByteBuffer>> values = ((MultiColumnRestriction.IN) r).splitValues(options);
TreeSet<CellName> inValues = new TreeSet<>(cfm.comparator);
for (List<ByteBuffer> components : values)
{
for (int i = 0; i < components.size(); i++)
if (components.get(i) == null)
throw new InvalidRequestException("Invalid null value in condition for column " + cfm.clusteringColumns().get(i + def.position()));
Composite prefix = builder.buildWith(components);
inValues.addAll(addSelectedColumns(prefix));
}
return inValues;
// we have a multi-column IN restriction
List<List<ByteBuffer>> values = ((MultiColumnRestriction.IN) r).splitValues(options);
TreeSet<CellName> inValues = new TreeSet<>(cfm.comparator);
for (List<ByteBuffer> components : values)
{
for (int i = 0; i < components.size(); i++)
if (components.get(i) == null)
throw new InvalidRequestException("Invalid null value in condition for column "
+ cfm.clusteringColumns().get(i + def.position()).name);
Composite prefix = builder.buildWith(components);
inValues.addAll(addSelectedColumns(prefix));
}
return inValues;
}
}
@ -827,38 +847,24 @@ public class SelectStatement implements CQLStatement
return false;
}
private static List<Composite> buildBound(Bound bound,
List<ColumnDefinition> defs,
Restriction[] restrictions,
boolean isReversed,
CType type,
QueryOptions options) throws InvalidRequestException
@VisibleForTesting
static List<Composite> buildBound(Bound bound,
List<ColumnDefinition> defs,
Restriction[] restrictions,
boolean isReversed,
CType type,
QueryOptions options) throws InvalidRequestException
{
CBuilder builder = type.builder();
// check the first restriction to see if we're dealing with a multi-column restriction
if (!defs.isEmpty())
{
Restriction firstRestriction = restrictions[0];
if (firstRestriction != null && firstRestriction.isMultiColumn())
{
if (firstRestriction.isSlice())
return buildMultiColumnSliceBound(bound, defs, (MultiColumnRestriction.Slice) firstRestriction, isReversed, builder, options);
else if (firstRestriction.isIN())
return buildMultiColumnInBound(bound, defs, (MultiColumnRestriction.IN) firstRestriction, isReversed, builder, type, options);
else
return buildMultiColumnEQBound(bound, defs, (MultiColumnRestriction.EQ) firstRestriction, isReversed, builder, options);
}
}
// The end-of-component of composite doesn't depend on whether the
// component type is reversed or not (i.e. the ReversedType is applied
// to the component comparator but not to the end-of-component itself),
// it only depends on whether the slice is reversed
Bound eocBound = isReversed ? Bound.reverse(bound) : bound;
for (Iterator<ColumnDefinition> iter = defs.iterator(); iter.hasNext();)
for (int i = 0, m = defs.size(); i < m; i++)
{
ColumnDefinition def = iter.next();
ColumnDefinition def = defs.get(i);
// In a restriction, we always have Bound.START < Bound.END for the "base" comparator.
// So if we're doing a reverse slice, we must inverse the bounds when giving them as start and end of the slice filter.
@ -875,36 +881,82 @@ public class SelectStatement implements CQLStatement
}
if (r.isSlice())
{
builder.add(getSliceValue(r, b, options));
if (r.isMultiColumn())
{
MultiColumnRestriction.Slice slice = (MultiColumnRestriction.Slice) r;
if (!slice.hasBound(b))
{
Composite prefix = builder.build();
return Collections.singletonList(builder.remainingCount() > 0 && eocBound == Bound.END
? prefix.end()
: prefix);
}
List<ByteBuffer> vals = slice.componentBounds(b, options);
for (int j = 0, n = vals.size(); j < n; j++)
addValue(builder, defs.get(i + j), vals.get(j)) ;
}
else
{
builder.add(getSliceValue(r, b, options));
}
Operator relType = ((Restriction.Slice)r).getRelation(eocBound, b);
return Collections.singletonList(builder.build().withEOC(eocForRelation(relType)));
}
else
if (r.isIN())
{
// IN or EQ
// The IN query might not have listed the values in comparator order, so we need to re-sort
// the bounds lists to make sure the slices works correctly (also, to avoid duplicates).
TreeSet<Composite> inValues = new TreeSet<>(isReversed ? type.reverseComparator() : type);
if (r.isMultiColumn())
{
List<List<ByteBuffer>> splitInValues = ((MultiColumnRestriction.IN) r).splitValues(options);
for (List<ByteBuffer> components : splitInValues)
{
for (int j = 0; j < components.size(); j++)
if (components.get(j) == null)
throw new InvalidRequestException("Invalid null value in condition for column " + defs.get(i + j).name);
Composite prefix = builder.buildWith(components);
inValues.add(builder.remainingCount() == 0 ? prefix : addEOC(prefix, eocBound));
}
return new ArrayList<>(inValues);
}
List<ByteBuffer> values = r.values(options);
if (values.size() != 1)
{
// IN query, we only support it on the clustering columns
assert def.position() == defs.size() - 1;
// The IN query might not have listed the values in comparator order, so we need to re-sort
// the bounds lists to make sure the slices works correctly (also, to avoid duplicates).
TreeSet<Composite> s = new TreeSet<>(isReversed ? type.reverseComparator() : type);
for (ByteBuffer val : values)
{
if (val == null)
throw new InvalidRequestException(String.format("Invalid null clustering key part %s", def.name));
throw new InvalidRequestException(String.format("Invalid null value in condition for column %s",
def.name));
Composite prefix = builder.buildWith(val);
// See below for why this
s.add(builder.remainingCount() == 0 ? prefix : (eocBound == Bound.END ? prefix.end() : prefix.start()));
inValues.add(builder.remainingCount() == 0 ? prefix : addEOC(prefix, eocBound));
}
return new ArrayList<>(s);
return new ArrayList<>(inValues);
}
}
ByteBuffer val = values.get(0);
if (val == null)
throw new InvalidRequestException(String.format("Invalid null clustering key part %s", def.name));
builder.add(val);
List<ByteBuffer> values = r.values(options);
if (r.isMultiColumn())
{
for (int j = 0; j < values.size(); j++)
addValue(builder, defs.get(i + j), values.get(j));
i += values.size() - 1; // skips the processed columns
}
else
{
addValue(builder, def, values.get(0));
}
}
// Means no relation at all or everything was an equal
@ -914,7 +966,34 @@ public class SelectStatement implements CQLStatement
// case using the eoc would be bad, since for the random partitioner we have no guarantee that
// prefix.end() will sort after prefix (see #5240).
Composite prefix = builder.build();
return Collections.singletonList(builder.remainingCount() == 0 ? prefix : (eocBound == Bound.END ? prefix.end() : prefix.start()));
return Collections.singletonList(builder.remainingCount() == 0 ? prefix : addEOC(prefix, eocBound));
}
/**
* Adds an EOC to the specified Composite.
*
* @param composite the composite
* @param eocBound the EOC bound
* @return a new <code>Composite</code> with the EOC corresponding to the eocBound
*/
private static Composite addEOC(Composite composite, Bound eocBound)
{
return eocBound == Bound.END ? composite.end() : composite.start();
}
/**
* Adds the specified value to the specified builder
*
* @param builder the CBuilder to which the value must be added
* @param def the column associated to the value
* @param value the value to add
* @throws InvalidRequestException if the value is null
*/
private static void addValue(CBuilder builder, ColumnDefinition def, ByteBuffer value) throws InvalidRequestException
{
if (value == null)
throw new InvalidRequestException(String.format("Invalid null value in condition for column %s", def.name));
builder.add(value);
}
private static Composite.EOC eocForRelation(Operator op)
@ -936,104 +1015,6 @@ public class SelectStatement implements CQLStatement
}
}
private static List<Composite> buildMultiColumnSliceBound(Bound bound,
List<ColumnDefinition> defs,
MultiColumnRestriction.Slice slice,
boolean isReversed,
CBuilder builder,
QueryOptions options) throws InvalidRequestException
{
Bound eocBound = isReversed ? Bound.reverse(bound) : bound;
Iterator<ColumnDefinition> iter = defs.iterator();
ColumnDefinition firstName = iter.next();
// A hack to preserve pre-6875 behavior for tuple-notation slices where the comparator mixes ASCENDING
// and DESCENDING orders. This stores the bound for the first component; we will re-use it for all following
// components, even if they don't match the first component's reversal/non-reversal. Note that this does *not*
// guarantee correct query results, it just preserves the previous behavior.
Bound firstComponentBound = isReversed == isReversedType(firstName) ? bound : Bound.reverse(bound);
if (!slice.hasBound(firstComponentBound))
{
Composite prefix = builder.build();
return Collections.singletonList(builder.remainingCount() > 0 && eocBound == Bound.END
? prefix.end()
: prefix);
}
List<ByteBuffer> vals = slice.componentBounds(firstComponentBound, options);
ByteBuffer v = vals.get(firstName.position());
if (v == null)
throw new InvalidRequestException("Invalid null value in condition for column " + firstName.name);
builder.add(v);
while (iter.hasNext())
{
ColumnDefinition def = iter.next();
if (def.position() >= vals.size())
break;
v = vals.get(def.position());
if (v == null)
throw new InvalidRequestException("Invalid null value in condition for column " + def.name);
builder.add(v);
}
Operator relType = slice.getRelation(eocBound, firstComponentBound);
return Collections.singletonList(builder.build().withEOC(eocForRelation(relType)));
}
private static List<Composite> buildMultiColumnInBound(Bound bound,
List<ColumnDefinition> defs,
MultiColumnRestriction.IN restriction,
boolean isReversed,
CBuilder builder,
CType type,
QueryOptions options) throws InvalidRequestException
{
List<List<ByteBuffer>> splitInValues = restriction.splitValues(options);
Bound eocBound = isReversed ? Bound.reverse(bound) : bound;
// The IN query might not have listed the values in comparator order, so we need to re-sort
// the bounds lists to make sure the slices works correctly (also, to avoid duplicates).
TreeSet<Composite> inValues = new TreeSet<>(isReversed ? type.reverseComparator() : type);
for (List<ByteBuffer> components : splitInValues)
{
for (int i = 0; i < components.size(); i++)
if (components.get(i) == null)
throw new InvalidRequestException("Invalid null value in condition for column " + defs.get(i));
Composite prefix = builder.buildWith(components);
inValues.add(eocBound == Bound.END && builder.remainingCount() - components.size() > 0
? prefix.end()
: prefix);
}
return new ArrayList<>(inValues);
}
private static List<Composite> buildMultiColumnEQBound(Bound bound,
List<ColumnDefinition> defs,
MultiColumnRestriction.EQ restriction,
boolean isReversed,
CBuilder builder,
QueryOptions options) throws InvalidRequestException
{
Bound eocBound = isReversed ? Bound.reverse(bound) : bound;
List<ByteBuffer> values = restriction.values(options);
for (int i = 0; i < values.size(); i++)
{
ByteBuffer component = values.get(i);
if (component == null)
throw new InvalidRequestException("Invalid null value in condition for column " + defs.get(i));
builder.add(component);
}
Composite prefix = builder.build();
return Collections.singletonList(builder.remainingCount() > 0 && eocBound == Bound.END
? prefix.end()
: prefix);
}
private static boolean isNullRestriction(Restriction r, Bound b)
{
return r == null || (r.isSlice() && !((Restriction.Slice)r).hasBound(b));
@ -1470,8 +1451,6 @@ public class SelectStatement implements CQLStatement
*/
boolean hasQueriableIndex = false;
boolean hasQueriableClusteringColumnIndex = false;
boolean hasSingleColumnRelations = false;
boolean hasMultiColumnRelations = false;
ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(columnFamily());
SecondaryIndexManager indexManager = cfs.indexManager;
@ -1490,7 +1469,6 @@ public class SelectStatement implements CQLStatement
hasQueriableIndex |= queriable[0];
hasQueriableClusteringColumnIndex |= queriable[1];
names.add(def);
hasMultiColumnRelations |= ColumnDefinition.Kind.CLUSTERING_COLUMN.equals(def.kind);
}
updateRestrictionsForRelation(stmt, names, rel, boundNames);
}
@ -1502,12 +1480,9 @@ public class SelectStatement implements CQLStatement
boolean[] queriable = processRelationEntity(stmt, indexManager, relation, entity, def);
hasQueriableIndex |= queriable[0];
hasQueriableClusteringColumnIndex |= queriable[1];
hasSingleColumnRelations |= ColumnDefinition.Kind.CLUSTERING_COLUMN.equals(def.kind);
updateRestrictionsForRelation(stmt, def, rel, boundNames);
}
}
if (hasSingleColumnRelations && hasMultiColumnRelations)
throw new InvalidRequestException("Mixing single column relations and multi column relations on clustering columns is not allowed");
// At this point, the select statement if fully constructed, but we still have a few things to validate
processPartitionKeyRestrictions(stmt, hasQueriableIndex, cfm);
@ -1601,16 +1576,19 @@ public class SelectStatement implements CQLStatement
{
List<ColumnDefinition> restrictedColumns = new ArrayList<>();
Set<ColumnDefinition> seen = new HashSet<>();
Restriction existing = null;
int previousPosition = -1;
for (ColumnDefinition def : defs)
int previousPosition = defs.get(0).position() - 1;
for (int i = 0, m = defs.size(); i < m; i++)
{
ColumnDefinition def = defs.get(i);
// ensure multi-column restriction only applies to clustering columns
if (def.kind != ColumnDefinition.Kind.CLUSTERING_COLUMN)
throw new InvalidRequestException(String.format("Multi-column relations can only be applied to clustering columns: %s", def));
throw new InvalidRequestException(String.format("Multi-column relations can only be applied to clustering columns: %s", def.name));
if (seen.contains(def))
throw new InvalidRequestException(String.format("Column \"%s\" appeared twice in a relation: %s", def, relation));
throw new InvalidRequestException(String.format("Column \"%s\" appeared twice in a relation: %s", def.name, relation));
seen.add(def);
// check that no clustering columns were skipped
@ -1620,20 +1598,53 @@ public class SelectStatement implements CQLStatement
throw new InvalidRequestException(String.format(
"Clustering columns may not be skipped in multi-column relations. " +
"They should appear in the PRIMARY KEY order. Got %s", relation));
else
throw new InvalidRequestException(String.format(
"Clustering columns must appear in the PRIMARY KEY order in multi-column relations: %s", relation));
throw new InvalidRequestException(String.format(
"Clustering columns must appear in the PRIMARY KEY order in multi-column relations: %s",
relation));
}
previousPosition++;
Restriction existing = getExistingRestriction(stmt, def);
Restriction previous = existing;
existing = getExistingRestriction(stmt, def);
Operator operator = relation.operator();
if (existing != null)
{
if (operator == Operator.EQ || operator == Operator.IN)
throw new InvalidRequestException(String.format("Column \"%s\" cannot be restricted by more than one relation if it is in an %s relation", def, relation.operator()));
{
throw new InvalidRequestException(String.format(
"Column \"%s\" cannot be restricted by more than one relation if it is in an %s relation",
def.name, operator));
}
else if (!existing.isSlice())
throw new InvalidRequestException(String.format("Column \"%s\" cannot be restricted by an equality relation and an inequality relation", def));
{
throw new InvalidRequestException(String.format(
"Column \"%s\" cannot be restricted by an equality relation and an inequality relation",
def.name));
}
else
{
if (!existing.isMultiColumn())
{
throw new InvalidRequestException(String.format(
"Column \"%s\" cannot have both tuple-notation inequalities and single-column inequalities: %s",
def.name, relation));
}
boolean existingRestrictionStartBefore =
(i == 0 && def.position() != 0 && stmt.columnRestrictions[def.position() - 1] == existing);
boolean existingRestrictionStartAfter = (i != 0 && previous != existing);
if (existingRestrictionStartBefore || existingRestrictionStartAfter)
{
throw new InvalidRequestException(String.format(
"Column \"%s\" cannot be restricted by two tuple-notation inequalities not starting with the same column: %s",
def.name, relation));
}
checkBound(existing, def, operator);
}
}
restrictedColumns.add(def);
}
@ -1685,14 +1696,13 @@ public class SelectStatement implements CQLStatement
{
Term t = relation.getValue().prepare(keyspace(), defs);
t.collectMarkerSpecification(boundNames);
Restriction.Slice restriction = (Restriction.Slice)getExistingRestriction(stmt, defs.get(0));
if (restriction == null)
restriction = new MultiColumnRestriction.Slice(false);
restriction.setBound(relation.operator(), t);
for (ColumnDefinition def : defs)
{
Restriction.Slice restriction = (Restriction.Slice)getExistingRestriction(stmt, def);
if (restriction == null)
restriction = new MultiColumnRestriction.Slice(false);
else if (!restriction.isMultiColumn())
throw new InvalidRequestException(String.format("Column \"%s\" cannot have both tuple-notation inequalities and single-column inequalities: %s", def.name, relation));
restriction.setBound(def.name, relation.operator(), t);
stmt.columnRestrictions[def.position()] = restriction;
}
break;
@ -1702,7 +1712,28 @@ public class SelectStatement implements CQLStatement
}
}
private Restriction getExistingRestriction(SelectStatement stmt, ColumnDefinition def)
/**
* Checks that the operator for the specified column is compatible with the bounds of the existing restriction.
*
* @param existing the existing restriction
* @param def the column definition
* @param operator the operator
* @throws InvalidRequestException if the operator is not compatible with the bounds of the existing restriction
*/
private static void checkBound(Restriction existing, ColumnDefinition def, Operator operator) throws InvalidRequestException
{
Restriction.Slice existingSlice = (Restriction.Slice) existing;
if (existingSlice.hasBound(Bound.START) && (operator == Operator.GT || operator == Operator.GTE))
throw new InvalidRequestException(String.format(
"More than one restriction was found for the start bound on %s", def.name));
if (existingSlice.hasBound(Bound.END) && (operator == Operator.LT || operator == Operator.LTE))
throw new InvalidRequestException(String.format(
"More than one restriction was found for the end bound on %s", def.name));
}
private static Restriction getExistingRestriction(SelectStatement stmt, ColumnDefinition def)
{
switch (def.kind)
{
@ -1817,9 +1848,11 @@ public class SelectStatement implements CQLStatement
// and the new one isn't since that would bypass that later test.
throw new InvalidRequestException("Only EQ and IN relation are supported on the partition key (unless you use the token() function)");
checkBound(existingRestriction, def, newRel.operator());
Term t = newRel.getValue().prepare(keyspace(), receiver);
t.collectMarkerSpecification(boundNames);
((SingleColumnRestriction.Slice)existingRestriction).setBound(def.name, newRel.operator(), t);
((SingleColumnRestriction.Slice)existingRestriction).setBound(newRel.operator(), t);
}
break;
case CONTAINS_KEY:
@ -1959,7 +1992,7 @@ public class SelectStatement implements CQLStatement
// the column is indexed that is.
boolean canRestrictFurtherComponents = true;
ColumnDefinition previous = null;
boolean previousIsSlice = false;
Restriction previousRestriction = null;
Iterator<ColumnDefinition> iter = cfm.clusteringColumns().iterator();
for (int i = 0; i < stmt.columnRestrictions.length; i++)
{
@ -1969,30 +2002,36 @@ public class SelectStatement implements CQLStatement
if (restriction == null)
{
canRestrictFurtherComponents = false;
previousIsSlice = false;
}
else if (!canRestrictFurtherComponents)
{
// We're here if the previous clustering column was either not restricted or was a slice.
// We can't restrict the current column unless:
// 1) we're in the special case of the 'tuple' notation from #4851 which we expand as multiple
// consecutive slices: in which case we're good with this restriction and we continue
// 2) we have a 2ndary index, in which case we have to use it but can skip more validation
if (!(previousIsSlice && restriction.isSlice() && restriction.isMultiColumn()))
// We're here if the previous clustering column was either not restricted, was a slice or an IN tulpe-notation.
// we can continue if we are in the special case of a slice 'tuple' notation from #4851
if (restriction != previousRestriction)
{
// if we have a 2ndary index, we need to use it
if (hasQueriableIndex)
{
stmt.usesSecondaryIndexing = true; // handle gaps and non-keyrange cases.
stmt.usesSecondaryIndexing = true;
break;
}
if (previousRestriction == null)
throw new InvalidRequestException(String.format(
"PRIMARY KEY column \"%s\" cannot be restricted (preceding column \"%s\" is not restricted)", cdef.name, previous.name));
if (previousRestriction.isMultiColumn() && previousRestriction.isIN())
throw new InvalidRequestException(String.format(
"PRIMARY KEY column \"%s\" cannot be restricted (preceding column \"%s\" is restricted by an IN tuple notation)", cdef.name, previous.name));
throw new InvalidRequestException(String.format(
"PRIMARY KEY column \"%s\" cannot be restricted (preceding column \"%s\" is either not restricted or by a non-EQ relation)", cdef.name, previous.name));
"PRIMARY KEY column \"%s\" cannot be restricted (preceding column \"%s\" is restricted by a non-EQ relation)", cdef.name, previous.name));
}
}
else if (restriction.isSlice())
{
canRestrictFurtherComponents = false;
previousIsSlice = true;
Restriction.Slice slice = (Restriction.Slice)restriction;
// For non-composite slices, we don't support internally the difference between exclusive and
// inclusive bounds, so we deal with it manually.
@ -2003,8 +2042,12 @@ public class SelectStatement implements CQLStatement
{
if (!restriction.isMultiColumn() && i != stmt.columnRestrictions.length - 1)
throw new InvalidRequestException(String.format("Clustering column \"%s\" cannot be restricted by an IN relation", cdef.name));
else if (stmt.selectACollection())
if (stmt.selectACollection())
throw new InvalidRequestException(String.format("Cannot restrict column \"%s\" by IN relation as a collection is selected by the query", cdef.name));
if (restriction.isMultiColumn())
canRestrictFurtherComponents = false;
}
else if (restriction.isContains())
{
@ -2014,6 +2057,7 @@ public class SelectStatement implements CQLStatement
}
previous = cdef;
previousRestriction = restriction;
}
}

View File

@ -87,9 +87,9 @@ public abstract class SingleColumnRestriction implements Restriction
public static class InWithValues extends SingleColumnRestriction implements Restriction.IN
{
protected final List<Term> values;
protected final List<? extends Term> values;
public InWithValues(List<Term> values)
public InWithValues(List<? extends Term> values)
{
this.values = values;
}
@ -292,7 +292,8 @@ public abstract class SingleColumnRestriction implements Restriction
throw new AssertionError();
}
public void setBound(ColumnIdentifier name, Operator operator, Term t) throws InvalidRequestException
@Override
public final void setBound(Operator operator, Term t) throws InvalidRequestException
{
Bound b;
boolean inclusive;
@ -318,9 +319,7 @@ public abstract class SingleColumnRestriction implements Restriction
throw new AssertionError();
}
if (bounds[b.idx] != null)
throw new InvalidRequestException(String.format(
"More than one restriction was found for the %s bound on %s", b.name().toLowerCase(), name));
assert bounds[b.idx] == null;
bounds[b.idx] = t;
boundInclusive[b.idx] = inclusive;

View File

@ -29,9 +29,12 @@ public class MultiColumnRelationTest extends CQLTester
createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a, b))" + compactOption);
assertInvalidSyntax("SELECT * FROM %s WHERE () = (?, ?)", 1, 2);
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (b) = (?) AND (b) > (?)", 0, 0);
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (b) > (?) AND (b) > (?)", 0, 1);
assertInvalid("SELECT * FROM %s WHERE (a, b) = (?, ?)", 0, 0);
assertInvalidMessage("Column \"b\" cannot be restricted by an equality relation and an inequality relation",
"SELECT * FROM %s WHERE a = 0 AND (b) = (?) AND (b) > (?)", 0, 0);
assertInvalidMessage("More than one restriction was found for the start bound on b",
"SELECT * FROM %s WHERE a = 0 AND (b) > (?) AND (b) > (?)", 0, 1);
assertInvalidMessage("Multi-column relations can only be applied to clustering columns: a",
"SELECT * FROM %s WHERE (a, b) = (?, ?)", 0, 0);
}
}
@ -43,39 +46,192 @@ public class MultiColumnRelationTest extends CQLTester
createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c, d))" + compactOption);
assertInvalidSyntax("SELECT * FROM %s WHERE a = 0 AND (b, c) > ()");
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (b, c) > (?, ?, ?)", 1, 2, 3);
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (b, c) > (?, ?)", 1, null);
assertInvalidMessage("Expected 2 elements in value tuple, but got 3: (?, ?, ?)",
"SELECT * FROM %s WHERE a = 0 AND (b, c) > (?, ?, ?)", 1, 2, 3);
assertInvalidMessage("Invalid null value in condition for column c",
"SELECT * FROM %s WHERE a = 0 AND (b, c) > (?, ?)", 1, null);
// Wrong order of columns
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (d, c, b) = (?, ?, ?)", 0, 0, 0);
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (d, c, b) > (?, ?, ?)", 0, 0, 0);
assertInvalidMessage("Clustering columns must appear in the PRIMARY KEY order in multi-column relations: (d, c, b) = (?, ?, ?)",
"SELECT * FROM %s WHERE a = 0 AND (d, c, b) = (?, ?, ?)", 0, 0, 0);
assertInvalidMessage("Clustering columns must appear in the PRIMARY KEY order in multi-column relations: (d, c, b) > (?, ?, ?)",
"SELECT * FROM %s WHERE a = 0 AND (d, c, b) > (?, ?, ?)", 0, 0, 0);
// Wrong number of values
assertInvalid("SELECT * FROM %s WHERE a=0 AND (b, c, d) IN ((?, ?))", 0, 1);
assertInvalid("SELECT * FROM %s WHERE a=0 AND (b, c, d) IN ((?, ?, ?, ?, ?))", 0, 1, 2, 3, 4);
assertInvalidMessage("Expected 3 elements in value tuple, but got 2: (?, ?)",
"SELECT * FROM %s WHERE a=0 AND (b, c, d) IN ((?, ?))", 0, 1);
assertInvalidMessage("Expected 3 elements in value tuple, but got 5: (?, ?, ?, ?, ?)",
"SELECT * FROM %s WHERE a=0 AND (b, c, d) IN ((?, ?, ?, ?, ?))", 0, 1, 2, 3, 4);
// Missing first clustering column
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (c, d) = (?, ?)", 0, 0);
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (c, d) > (?, ?)", 0, 0);
assertInvalidMessage("PRIMARY KEY column \"c\" cannot be restricted (preceding column \"b\" is not restricted)",
"SELECT * FROM %s WHERE a = 0 AND (c, d) = (?, ?)", 0, 0);
assertInvalidMessage("PRIMARY KEY column \"c\" cannot be restricted (preceding column \"b\" is not restricted)",
"SELECT * FROM %s WHERE a = 0 AND (c, d) > (?, ?)", 0, 0);
// Nulls
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (b, c, d) IN ((?, ?, ?))", 1, 2, null);
assertInvalidMessage("Invalid null value in condition for column d",
"SELECT * FROM %s WHERE a = 0 AND (b, c, d) IN ((?, ?, ?))", 1, 2, null);
// Wrong type for 'd'
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (b, c, d) = (?, ?, ?)", 1, 2, "foobar");
assertInvalidMessage("Expected 4 or 0 byte int (6)",
"SELECT * FROM %s WHERE a = 0 AND (b, c, d) = (?, ?, ?)", 1, 2, "foobar");
assertInvalid("SELECT * FROM %s WHERE a = 0 AND b = (?, ?, ?)", 1, 2, 3);
assertInvalidMessage("Invalid tuple type literal for b of type int",
"SELECT * FROM %s WHERE a = 0 AND b = (?, ?, ?)", 1, 2, 3);
// Mix single and tuple inequalities
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (b, c, d) > (?, ?, ?) AND b < ?", 0, 1, 0, 1);
assertInvalid("SELECT * FROM %s WHERE a = 0 AND (b, c, d) > (?, ?, ?) AND c < ?", 0, 1, 0, 1);
assertInvalid("SELECT * FROM %s WHERE a = 0 AND b > ? AND (b, c, d) < (?, ?, ?)", 1, 1, 1, 0);
assertInvalid("SELECT * FROM %s WHERE a = 0 AND c > ? AND (b, c, d) < (?, ?, ?)", 1, 1, 1, 0);
assertInvalidMessage("Column \"b\" cannot be restricted by both a tuple notation inequality and a single column inequality (b < ?)",
"SELECT * FROM %s WHERE a = 0 AND (b, c, d) > (?, ?, ?) AND b < ?", 0, 1, 0, 1);
assertInvalidMessage("Column \"c\" cannot be restricted by both a tuple notation inequality and a single column inequality (c < ?)",
"SELECT * FROM %s WHERE a = 0 AND (b, c, d) > (?, ?, ?) AND c < ?", 0, 1, 0, 1);
assertInvalidMessage("Column \"b\" cannot have both tuple-notation inequalities and single-column inequalities: (b, c, d) < (?, ?, ?)",
"SELECT * FROM %s WHERE a = 0 AND b > ? AND (b, c, d) < (?, ?, ?)", 1, 1, 1, 0);
assertInvalidMessage("Column \"c\" cannot have both tuple-notation inequalities and single-column inequalities: (b, c, d) < (?, ?, ?)",
"SELECT * FROM %s WHERE a = 0 AND c > ? AND (b, c, d) < (?, ?, ?)", 1, 1, 1, 0);
assertInvalid("SELECT * FROM %s WHERE (a, b, c, d) IN ((?, ?, ?, ?))", 0, 1, 2, 3);
assertInvalid("SELECT * FROM %s WHERE (c, d) IN ((?, ?))", 0, 1);
assertInvalidMessage("Multi-column relations can only be applied to clustering columns: a",
"SELECT * FROM %s WHERE (a, b, c, d) IN ((?, ?, ?, ?))", 0, 1, 2, 3);
assertInvalidMessage("PRIMARY KEY column \"c\" cannot be restricted (preceding column \"b\" is not restricted)",
"SELECT * FROM %s WHERE (c, d) IN ((?, ?))", 0, 1);
assertInvalidMessage("PRIMARY KEY column \"c\" cannot be restricted (preceding column \"b\" is restricted by a non-EQ relation)",
"SELECT * FROM %s WHERE a = ? AND b > ? AND (c, d) IN ((?, ?))", 0, 0, 0, 0);
assertInvalid("SELECT * FROM %s WHERE a = ? AND (b, c) in ((?, ?), (?, ?)) AND d > ?", 0, 0, 0, 0, 0, 0);
assertInvalidMessage("PRIMARY KEY column \"c\" cannot be restricted (preceding column \"b\" is restricted by a non-EQ relation)",
"SELECT * FROM %s WHERE a = ? AND b > ? AND (c, d) > (?, ?)", 0, 0, 0, 0);
assertInvalidMessage("PRIMARY KEY column \"c\" cannot be restricted (preceding column \"b\" is restricted by a non-EQ relation)",
"SELECT * FROM %s WHERE a = ? AND (c, d) > (?, ?) AND b > ? ", 0, 0, 0, 0);
assertInvalidMessage("Column \"c\" cannot be restricted by two tuple-notation inequalities not starting with the same column: (c) < (?)",
"SELECT * FROM %s WHERE a = ? AND (b, c) > (?, ?) AND (b) < (?) AND (c) < (?)", 0, 0, 0, 0, 0);
assertInvalidMessage("Column \"c\" cannot be restricted by two tuple-notation inequalities not starting with the same column: (b, c) > (?, ?)",
"SELECT * FROM %s WHERE a = ? AND (c) < (?) AND (b, c) > (?, ?) AND (b) < (?)", 0, 0, 0, 0, 0);
assertInvalidMessage("Column \"c\" cannot be restricted by two tuple-notation inequalities not starting with the same column: (b, c) > (?, ?)",
"SELECT * FROM %s WHERE a = ? AND (b) < (?) AND (c) < (?) AND (b, c) > (?, ?)", 0, 0, 0, 0, 0);
assertInvalidMessage("Column \"c\" cannot be restricted by two tuple-notation inequalities not starting with the same column: (c) < (?)",
"SELECT * FROM %s WHERE a = ? AND (b, c) > (?, ?) AND (c) < (?)", 0, 0, 0, 0);
assertInvalidMessage("PRIMARY KEY column \"d\" cannot be restricted (preceding column \"c\" is restricted by an IN tuple notation)",
"SELECT * FROM %s WHERE a = ? AND (b, c) in ((?, ?), (?, ?)) AND d > ?", 0, 0, 0, 0, 0, 0);
}
}
@Test
public void testMultiAndSingleColumnRelationMix() throws Throwable
{
for (String compactOption : new String[]{"", " WITH COMPACT STORAGE"})
{
createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c, d))" + compactOption);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 0, 0);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 1, 0);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 1, 1);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 0, 0);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 0);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 1);
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c, d) = (?, ?)", 0, 1, 0, 0),
row(0, 1, 0, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c) IN ((?))", 0, 1, 0),
row(0, 1, 0, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c) IN ((?), (?))", 0, 1, 0, 1),
row(0, 1, 0, 0),
row(0, 1, 1, 0),
row(0, 1, 1, 1));
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c, d) IN ((?, ?))", 0, 1, 0, 0),
row(0, 1, 0, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c, d) IN ((?, ?), (?, ?))", 0, 1, 0, 0, 1, 1),
row(0, 1, 0, 0),
row(0, 1, 1, 1));
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c, d) > (?, ?)", 0, 1, 0, 0),
row(0, 1, 1, 0),
row(0, 1, 1, 1));
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c, d) > (?, ?) and (c) <= (?) ", 0, 1, 0, 0, 1),
row(0, 1, 1, 0),
row(0, 1, 1, 1));
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c, d) >= (?, ?) and (c, d) < (?, ?)", 0, 1, 0, 0, 1, 1),
row(0, 1, 0, 0),
row(0, 1, 1, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b, c) = (?, ?) and d = ?", 0, 0, 1, 0),
row(0, 0, 1, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c) = (?) and d = ?", 0, 0, 1, 0),
row(0, 0, 1, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b, c) = (?, ?) and d IN (?, ?)", 0, 0, 1, 0, 2),
row(0, 0, 1, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and b = ? and (c) = (?) and d IN (?, ?)", 0, 0, 1, 0, 2),
row(0, 0, 1, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b, c) = (?, ?) and d >= ?", 0, 0, 1, 0),
row(0, 0, 1, 0),
row(0, 0, 1, 1));
assertRows(execute("SELECT * FROM %s WHERE a = ? and d < 1 and (b, c) = (?, ?) and d >= ?", 0, 0, 1, 0),
row(0, 0, 1, 0));
}
}
@Test
public void testMultipleMultiColumnRelation() throws Throwable
{
for (String compactOption : new String[]{"", " WITH COMPACT STORAGE"})
{
createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c, d))" + compactOption);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 0, 0);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 1, 0);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 1, 1);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 0, 0);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 0);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 1);
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b) = (?) and (c, d) = (?, ?)", 0, 1, 0, 0),
row(0, 1, 0, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b) = (?) and (c) = (?) and (d) = (?)", 0, 1, 0, 0),
row(0, 1, 0, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b) = (?) and (c) IN ((?))", 0, 1, 0),
row(0, 1, 0, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b) = (?) and (c) IN ((?), (?))", 0, 1, 0, 1),
row(0, 1, 0, 0),
row(0, 1, 1, 0),
row(0, 1, 1, 1));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b) = (?) and (c, d) IN ((?, ?))", 0, 1, 0, 0),
row(0, 1, 0, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b) = (?) and (c, d) IN ((?, ?), (?, ?))", 0, 1, 0, 0, 1, 1),
row(0, 1, 0, 0),
row(0, 1, 1, 1));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b) = (?) and (c, d) > (?, ?)", 0, 1, 0, 0),
row(0, 1, 1, 0),
row(0, 1, 1, 1));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b) = (?) and (c, d) > (?, ?) and (c) <= (?) ", 0, 1, 0, 0, 1),
row(0, 1, 1, 0),
row(0, 1, 1, 1));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b) = (?) and (c, d) >= (?, ?) and (c, d) < (?, ?)", 0, 1, 0, 0, 1, 1),
row(0, 1, 0, 0),
row(0, 1, 1, 0));
assertRows(execute("SELECT * FROM %s WHERE a = ? and (b, c) = (?, ?) and (d) = (?)", 0, 0, 1, 0),
row(0, 0, 1, 0));
}
}

View File

@ -0,0 +1,965 @@
/*
* 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.statements;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.cql3.Term.MultiItemTerminal;
import org.apache.cassandra.db.ColumnFamilyType;
import org.apache.cassandra.db.composites.Composite;
import org.apache.cassandra.db.composites.Composite.EOC;
import org.apache.cassandra.db.composites.Composites;
import org.apache.cassandra.db.composites.CompoundSparseCellNameType;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.utils.ByteBufferUtil;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
public class SelectStatementTest
{
@Test
public void testBuildBoundWithNoRestrictions() throws InvalidRequestException
{
Restriction[] restrictions = new Restriction[2];
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
}
/**
* Test 'clustering_0 = 1' with only one clustering column
*/
@Test
public void testBuildBoundWithOneEqRestrictionsAndOneClusteringColumn() throws InvalidRequestException
{
ByteBuffer clustering_0 = ByteBufferUtil.bytes(1);
SingleColumnRestriction.EQ eq = new SingleColumnRestriction.EQ(toTerm(clustering_0), false);
Restriction[] restrictions = new Restriction[] { eq };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), clustering_0, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), clustering_0, EOC.END);
}
/**
* Test 'clustering_1 = 1' with 2 clustering columns
*/
@Test
public void testBuildBoundWithOneEqRestrictionsAndTwoClusteringColumns() throws InvalidRequestException
{
ByteBuffer clustering_2 = ByteBufferUtil.bytes(1);
SingleColumnRestriction.EQ eq = new SingleColumnRestriction.EQ(toTerm(clustering_2), false);
Restriction[] restrictions = new Restriction[] { eq, null };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), clustering_2, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), clustering_2, EOC.END);
}
/**
* Test 'clustering_0 IN (1, 2, 3)' with only one clustering column
*/
@Test
public void testBuildBoundWithOneInRestrictionsAndOneClusteringColumn() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
ByteBuffer value3 = ByteBufferUtil.bytes(3);
SingleColumnRestriction.IN in = new SingleColumnRestriction.InWithValues(toTerms(value1, value2, value3));
Restriction[] restrictions = new Restriction[] { in };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(3, bounds.size());
assertComposite(bounds.get(0), value1, EOC.START);
assertComposite(bounds.get(1), value2, EOC.START);
assertComposite(bounds.get(2), value3, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(3, bounds.size());
assertComposite(bounds.get(0), value1, EOC.END);
assertComposite(bounds.get(1), value2, EOC.END);
assertComposite(bounds.get(2), value3, EOC.END);
}
/**
* Test slice restriction (e.g 'clustering_0 > 1') with only one clustering column
*/
@Test
public void testBuildBoundWithSliceRestrictionsAndOneClusteringColumn() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
SingleColumnRestriction.Slice slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.GT, toTerm(value1));
Restriction[] restrictions = new Restriction[] { slice };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.GTE, toTerm(value1));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.NONE);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.LTE, toTerm(value1));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.END);
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.LT, toTerm(value1));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.START);
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.GT, toTerm(value1));
slice.setBound(Operator.LT, toTerm(value2));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value2, EOC.START);
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.GTE, toTerm(value1));
slice.setBound(Operator.LTE, toTerm(value1));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.NONE);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.END);
}
/**
* Test 'clustering_0 = 1 AND clustering_1 IN (1, 2, 3)' with two clustering columns
*/
@Test
public void testBuildBoundWithEqAndInRestrictions() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
ByteBuffer value3 = ByteBufferUtil.bytes(3);
SingleColumnRestriction.EQ eq = new SingleColumnRestriction.EQ(toTerm(value1), false);
SingleColumnRestriction.IN in = new SingleColumnRestriction.InWithValues(toTerms(value1, value2, value3));
Restriction[] restrictions = new Restriction[] { eq, in };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(3, bounds.size());
assertComposite(bounds.get(0), value1, value1, EOC.START);
assertComposite(bounds.get(1), value1, value2, EOC.START);
assertComposite(bounds.get(2), value1, value3, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(3, bounds.size());
assertComposite(bounds.get(0), value1, value1, EOC.END);
assertComposite(bounds.get(1), value1, value2, EOC.END);
assertComposite(bounds.get(2), value1, value3, EOC.END);
}
/**
* Test slice restriction (e.g 'clustering_0 > 1') with only one clustering column
*/
@Test
public void testBuildBoundWithEqAndSliceRestrictions() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
ByteBuffer value3 = ByteBufferUtil.bytes(3);
SingleColumnRestriction.EQ eq = new SingleColumnRestriction.EQ(toTerm(value3), false);
SingleColumnRestriction.Slice slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.GT, toTerm(value1));
Restriction[] restrictions = new Restriction[] { eq, slice };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, value1, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, EOC.END);
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.GTE, toTerm(value1));
restrictions = new Restriction[] { eq, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, value1, EOC.NONE);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, EOC.END);
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.LTE, toTerm(value1));
restrictions = new Restriction[] { eq, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, value1, EOC.END);
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.LT, toTerm(value1));
restrictions = new Restriction[] { eq, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, value1, EOC.START);
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.GT, toTerm(value1));
slice.setBound(Operator.LT, toTerm(value2));
restrictions = new Restriction[] { eq, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, value1, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, value2, EOC.START);
slice = new SingleColumnRestriction.Slice(false);
slice.setBound(Operator.GTE, toTerm(value1));
slice.setBound(Operator.LTE, toTerm(value1));
restrictions = new Restriction[] { eq, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, value1, EOC.NONE);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value3, value1, EOC.END);
}
/**
* Test '(clustering_0, clustering_1) = (1, 2)' with two clustering column
*/
@Test
public void testBuildBoundWithMultiEqRestrictions() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
MultiColumnRestriction.EQ eq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false);
Restriction[] restrictions = new Restriction[] { eq, eq };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.END);
}
/**
* Test '(clustering_0, clustering_1) IN ((1, 2), (2, 3))' with two clustering column
*/
@Test
public void testBuildBoundWithMultiInRestrictions() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
ByteBuffer value3 = ByteBufferUtil.bytes(3);
List<MultiItemTerminal> terms = asList(toMultiItemTerminal(value1, value2), toMultiItemTerminal(value2, value3));
MultiColumnRestriction.IN in = new MultiColumnRestriction.InWithValues(terms);
Restriction[] restrictions = new Restriction[] { in, in };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(2, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.START);
assertComposite(bounds.get(1), value2, value3, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(2, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.END);
assertComposite(bounds.get(1), value2, value3, EOC.END);
}
/**
* Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column
*/
@Test
public void testBuildBoundWithMultiSliceRestrictionsWithOneClusteringColumn() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
MultiColumnRestriction.Slice slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.GT, toMultiItemTerminal(value1));
Restriction[] restrictions = new Restriction[] { slice };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.GTE, toMultiItemTerminal(value1));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.NONE);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.LTE, toMultiItemTerminal(value1));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.END);
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.LT, toMultiItemTerminal(value1));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.START);
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.GT, toMultiItemTerminal(value1));
slice.setBound(Operator.LT, toMultiItemTerminal(value2));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value2, EOC.START);
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.GTE, toMultiItemTerminal(value1));
slice.setBound(Operator.LTE, toMultiItemTerminal(value2));
restrictions = new Restriction[] { slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.NONE);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value2, EOC.END);
}
/**
* Test multi-column slice restrictions (e.g '(clustering_0, clustering_1) > (1, 2)') with only one clustering
* column
*/
@Test
public void testBuildBoundWithMultiSliceRestrictionsWithTwoClusteringColumn() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
// (clustering_0, clustering1) > (1, 2)
MultiColumnRestriction.Slice slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.GT, toMultiItemTerminal(value1, value2));
Restriction[] restrictions = new Restriction[] { slice, slice };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
// (clustering_0, clustering1) >= (1, 2)
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.GTE, toMultiItemTerminal(value1, value2));
restrictions = new Restriction[] { slice, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.NONE);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
// (clustering_0, clustering1) <= (1, 2)
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.LTE, toMultiItemTerminal(value1, value2));
restrictions = new Restriction[] { slice, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.END);
// (clustering_0, clustering1) < (1, 2)
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.LT, toMultiItemTerminal(value1, value2));
restrictions = new Restriction[] { slice, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertEmptyComposite(bounds.get(0));
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.START);
// (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.GT, toMultiItemTerminal(value1, value2));
slice.setBound(Operator.LT, toMultiItemTerminal(value2));
restrictions = new Restriction[] { slice, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value2, EOC.START);
// (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
slice = new MultiColumnRestriction.Slice(false);
slice.setBound(Operator.GTE, toMultiItemTerminal(value1, value2));
slice.setBound(Operator.LTE, toMultiItemTerminal(value2, value1));
restrictions = new Restriction[] { slice, slice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.NONE);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value2, value1, EOC.END);
}
/**
* Test mixing single and multi equals restrictions (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3))
*/
@Test
public void testBuildBoundWithSingleEqAndMultiEqRestrictions() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
ByteBuffer value3 = ByteBufferUtil.bytes(3);
ByteBuffer value4 = ByteBufferUtil.bytes(4);
// clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3)
SingleColumnRestriction.EQ singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false);
MultiColumnRestriction.EQ multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value2, value3), false);
Restriction[] restrictions = new Restriction[] { singleEq, multiEq, multiEq };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.END);
// clustering_0 = 1 AND clustering_1 = 2 AND (clustering_2, clustering_3) = (3, 4)
singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false);
SingleColumnRestriction.EQ singleEq2 = new SingleColumnRestriction.EQ(toTerm(value2), false);
multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value3, value4), false);
restrictions = new Restriction[] { singleEq, singleEq2, multiEq, multiEq };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
// (clustering_0, clustering_1) = (1, 2) AND clustering_2 = 3
singleEq = new SingleColumnRestriction.EQ(toTerm(value3), false);
multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false);
restrictions = new Restriction[] { multiEq, multiEq, singleEq };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.END);
// clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3) AND clustering_3 = 4
singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false);
singleEq2 = new SingleColumnRestriction.EQ(toTerm(value4), false);
multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value2, value3), false);
restrictions = new Restriction[] { singleEq, multiEq, multiEq, singleEq2 };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
}
/**
* Test clustering_0 = 1 AND (clustering_1, clustering_2) IN ((2, 3), (4, 5))
*/
@Test
public void testBuildBoundWithSingleEqAndMultiINRestrictions() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
ByteBuffer value3 = ByteBufferUtil.bytes(3);
ByteBuffer value4 = ByteBufferUtil.bytes(4);
ByteBuffer value5 = ByteBufferUtil.bytes(5);
// clustering_0 = 1 AND (clustering_1, clustering_2) IN ((2, 3), (4, 5))
SingleColumnRestriction.EQ singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false);
MultiColumnRestriction.IN multiIn =
new MultiColumnRestriction.InWithValues(asList(toMultiItemTerminal(value2, value3),
toMultiItemTerminal(value4, value5)));
Restriction[] restrictions = new Restriction[] { singleEq, multiIn, multiIn };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(2, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.START);
assertComposite(bounds.get(1), value1, value4, value5, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(2, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.END);
assertComposite(bounds.get(1), value1, value4, value5, EOC.END);
// clustering_0 = 1 AND (clustering_1, clustering_2) IN ((2, 3))
singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false);
multiIn = new MultiColumnRestriction.InWithValues(asList(toMultiItemTerminal(value2, value3),
toMultiItemTerminal(value4, value5)));
restrictions = new Restriction[] { singleEq, multiIn, multiIn };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(2, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.START);
assertComposite(bounds.get(1), value1, value4, value5, EOC.START);
// clustering_0 = 1 AND clustering_1 = 5 AND (clustering_2, clustering_3) IN ((2, 3), (4, 5))
singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false);
SingleColumnRestriction.EQ singleEq2 = new SingleColumnRestriction.EQ(toTerm(value5), false);
multiIn = new MultiColumnRestriction.InWithValues(asList(toMultiItemTerminal(value2, value3),
toMultiItemTerminal(value4, value5)));
restrictions = new Restriction[] { singleEq, singleEq2, multiIn, multiIn };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(2, bounds.size());
assertComposite(bounds.get(0), value1, value5, value2, value3, EOC.START);
assertComposite(bounds.get(1), value1, value5, value4, value5, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(2, bounds.size());
assertComposite(bounds.get(0), value1, value5, value2, value3, EOC.END);
assertComposite(bounds.get(1), value1, value5, value4, value5, EOC.END);
}
/**
* Test mixing single equal restrictions with multi-column slice restrictions
* (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3))
*/
@Test
public void testBuildBoundWithSingleEqAndSliceRestrictions() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
ByteBuffer value3 = ByteBufferUtil.bytes(3);
ByteBuffer value4 = ByteBufferUtil.bytes(4);
ByteBuffer value5 = ByteBufferUtil.bytes(5);
// clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3)
SingleColumnRestriction.EQ singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false);
MultiColumnRestriction.Slice multiSlice = new MultiColumnRestriction.Slice(false);
multiSlice.setBound(Operator.GT, toMultiItemTerminal(value2, value3));
Restriction[] restrictions = new Restriction[] { singleEq, multiSlice, multiSlice };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, EOC.END);
// clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3) AND (clustering_1) < (4)
singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false);
multiSlice = new MultiColumnRestriction.Slice(false);
multiSlice.setBound(Operator.GT, toMultiItemTerminal(value2, value3));
multiSlice.setBound(Operator.LT, toMultiItemTerminal(value4));
restrictions = new Restriction[] { singleEq, multiSlice, multiSlice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value4, EOC.START);
// clustering_0 = 1 AND (clustering_1, clustering_2) => (2, 3) AND (clustering_1, clustering_2) <= (4, 5)
singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false);
multiSlice = new MultiColumnRestriction.Slice(false);
multiSlice.setBound(Operator.GTE, toMultiItemTerminal(value2, value3));
multiSlice.setBound(Operator.LTE, toMultiItemTerminal(value4, value5));
restrictions = new Restriction[] { singleEq, multiSlice, multiSlice };
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.NONE);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value4, value5, EOC.END);
}
/**
* Test mixing multi equal restrictions with single-column slice restrictions
* (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3))
*/
@Test
public void testBuildBoundWithMultiEqAndSingleSliceRestrictions() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
ByteBuffer value3 = ByteBufferUtil.bytes(3);
// (clustering_0, clustering_1) = (1, 2) AND clustering_2 > 3
MultiColumnRestriction.EQ multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false);
SingleColumnRestriction.Slice singleSlice = new SingleColumnRestriction.Slice(false);
singleSlice.setBound(Operator.GT, toTerm(value3));
Restriction[] restrictions = new Restriction[] { multiEq, multiEq, singleSlice };
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.END);
}
@Test
public void testBuildBoundWithSeveralMultiColumnRestrictions() throws InvalidRequestException
{
ByteBuffer value1 = ByteBufferUtil.bytes(1);
ByteBuffer value2 = ByteBufferUtil.bytes(2);
ByteBuffer value3 = ByteBufferUtil.bytes(3);
ByteBuffer value4 = ByteBufferUtil.bytes(4);
ByteBuffer value5 = ByteBufferUtil.bytes(5);
// (clustering_0, clustering_1) = (1, 2) AND (clustering_2, clustering_3) > (3, 4)
MultiColumnRestriction.EQ multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false);
MultiColumnRestriction.Slice multiSlice = new MultiColumnRestriction.Slice(false);
multiSlice.setBound(Operator.GT, toMultiItemTerminal(value3, value4));
Restriction[] restrictions = new Restriction[] { multiEq, multiEq, multiSlice, multiSlice};
List<Composite> bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, EOC.END);
// (clustering_0, clustering_1) = (1, 2) AND (clustering_2, clustering_3) IN ((3, 4), (4, 5))
multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false);
MultiColumnRestriction.IN multiIn =
new MultiColumnRestriction.InWithValues(asList(toMultiItemTerminal(value3, value4),
toMultiItemTerminal(value4, value5)));
restrictions = new Restriction[] { multiEq, multiEq, multiIn, multiIn};
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(2, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
assertComposite(bounds.get(1), value1, value2, value4, value5, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(2, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
assertComposite(bounds.get(1), value1, value2, value4, value5, EOC.END);
// (clustering_0, clustering_1) = (1, 2) AND (clustering_2, clustering_3) IN ((3, 4), (4, 5))
multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false);
MultiColumnRestriction.EQ multiEq2 = new MultiColumnRestriction.EQ(toMultiItemTerminal(value3, value4), false);
restrictions = new Restriction[] { multiEq, multiEq, multiEq2, multiEq2};
bounds = executeBuildBound(restrictions, Bound.START);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
bounds = executeBuildBound(restrictions, Bound.END);
assertEquals(1, bounds.size());
assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
}
/**
* Asserts that the specified <code>Composite</code> is an empty one.
*
* @param composite the composite to check
*/
private static void assertEmptyComposite(Composite composite)
{
assertEquals(Composites.EMPTY, composite);
}
/**
* Asserts that the specified <code>Composite</code> contains the specified element and the specified EOC.
*
* @param composite the composite to check
* @param element the expected element of the composite
* @param eoc the expected EOC of the composite
*/
private static void assertComposite(Composite composite, ByteBuffer element, EOC eoc)
{
assertComposite(composite, eoc, element);
}
/**
* Asserts that the specified <code>Composite</code> contains the 2 specified element and the specified EOC.
*
* @param composite the composite to check
* @param eoc the expected EOC of the composite
* @param elements the expected element of the composite
*/
private static void assertComposite(Composite composite, ByteBuffer firstElement, ByteBuffer secondElement, EOC eoc)
{
assertComposite(composite, eoc, firstElement, secondElement);
}
/**
* Asserts that the specified <code>Composite</code> contains the 3 specified element and the specified EOC.
*
* @param composite the composite to check
* @param firstElement the first expected element of the composite
* @param secondElement the second expected element of the composite
* @param thirdElement the third expected element of the composite
* @param eoc the expected EOC of the composite
* @param elements the expected element of the composite
*/
private static void assertComposite(Composite composite,
ByteBuffer firstElement,
ByteBuffer secondElement,
ByteBuffer thirdElement,
EOC eoc)
{
assertComposite(composite, eoc, firstElement, secondElement, thirdElement);
}
/**
* Asserts that the specified <code>Composite</code> contains the 4 specified element and the specified EOC.
*
* @param composite the composite to check
* @param firstElement the first expected element of the composite
* @param secondElement the second expected element of the composite
* @param thirdElement the third expected element of the composite
* @param fourthElement the fourth expected element of the composite
* @param eoc the expected EOC of the composite
* @param elements the expected element of the composite
*/
private static void assertComposite(Composite composite,
ByteBuffer firstElement,
ByteBuffer secondElement,
ByteBuffer thirdElement,
ByteBuffer fourthElement,
EOC eoc)
{
assertComposite(composite, eoc, firstElement, secondElement, thirdElement, fourthElement);
}
/**
* Asserts that the specified <code>Composite</code> contains the specified elements and EOC.
*
* @param composite the composite to check
* @param eoc the expected EOC of the composite
* @param elements the expected elements of the composite
*/
private static void assertComposite(Composite composite, EOC eoc, ByteBuffer... elements)
{
assertEquals("the composite size is not the expected one:", elements.length, composite.size());
for (int i = 0, m = elements.length; i < m; i++)
{
ByteBuffer element = elements[i];
assertEquals("the element " + i + " of the composite is not the expected one:", element, composite.get(i));
}
assertEquals("the EOC of the composite is not the expected one:", eoc, composite.eoc());
}
/**
* Calls the <code>SelectStatement.buildBound</code> with the specified restrictions.
*
* @param restrictions the restrictions
* @return the result from the method call to <code>SelectStatement.buildBound</code>
* @throws InvalidRequestException if the method call throw an exception
*/
private static List<Composite> executeBuildBound(Restriction[] restrictions,
Bound bound) throws InvalidRequestException
{
List<AbstractType<?>> types = new ArrayList<>();
for (int i = 0, m = restrictions.length; i < m; i++)
types.add(Int32Type.instance);
CompoundSparseCellNameType cType = new CompoundSparseCellNameType(types);
CFMetaData cfMetaData = new CFMetaData("keyspace", "test", ColumnFamilyType.Standard, cType);
List<ColumnDefinition> columnDefs = new ArrayList<>();
for (int i = 0, m = restrictions.length; i < m; i++)
{
ByteBuffer name = ByteBufferUtil.bytes("clustering_" + i);
columnDefs.add(ColumnDefinition.clusteringKeyDef(cfMetaData, name, types.get(i), i));
}
return SelectStatement.buildBound(bound, columnDefs, restrictions, false, cType, QueryOptions.DEFAULT);
}
/**
* Converts the specified values into a <code>MultiItemTerminal</code>.
*
* @param values the values to convert.
* @return the term corresponding to the specified values.
*/
private static MultiItemTerminal toMultiItemTerminal(ByteBuffer... values)
{
return new Tuples.Value(values);
}
/**
* Converts the specified value into a term.
*
* @param value the value to convert.
* @return the term corresponding to the specified value.
*/
private static Term toTerm(ByteBuffer value)
{
return new Constants.Value(value);
}
/**
* Converts the specified values into a <code>List</code> of terms.
*
* @param values the values to convert.
* @return a <code>List</code> of terms corresponding to the specified values.
*/
private static List<Term> toTerms(ByteBuffer... values)
{
List<Term> terms = new ArrayList<>();
for (ByteBuffer value : values)
terms.add(toTerm(value));
return terms;
}
}