mirror of https://github.com/apache/cassandra
Merge branch 'cassandra-2.0' into cassandra-2.1
This commit is contained in:
commit
9c7a601bbd
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue