You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ty...@apache.org on 2015/01/07 21:06:39 UTC
cassandra git commit: Allow mixing token and partition key
restrictions
Repository: cassandra
Updated Branches:
refs/heads/trunk 0a80fe4b5 -> 493859bf6
Allow mixing token and partition key restrictions
Patch by Benjamin Lerer; reviewed by Tyler Hobbs for CASSANDRA-7016
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/493859bf
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/493859bf
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/493859bf
Branch: refs/heads/trunk
Commit: 493859bf617ac80f560d02ad6d471aefd6a0ef91
Parents: 0a80fe4
Author: blerer <b_...@hotmail.com>
Authored: Wed Jan 7 14:05:38 2015 -0600
Committer: Tyler Hobbs <ty...@datastax.com>
Committed: Wed Jan 7 14:06:28 2015 -0600
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../apache/cassandra/cql3/TokenRelation.java | 4 +-
.../AbstractPrimaryKeyRestrictions.java | 12 +
.../restrictions/MultiColumnRestriction.java | 4 +-
.../SingleColumnPrimaryKeyRestrictions.java | 16 +-
.../restrictions/StatementRestrictions.java | 5 +-
.../cql3/restrictions/TokenFilter.java | 237 +++++++++++++++++++
.../cql3/restrictions/TokenRestriction.java | 57 +++--
.../cql3/SelectWithTokenFunctionTest.java | 139 ++++++++++-
9 files changed, 439 insertions(+), 36 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/493859bf/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 8ccc014..9f946a3 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
3.0
+ * Allow mixing token and partition key restrictions (CASSANDRA-7016)
* Support index key/value entries on map collections (CASSANDRA-8473)
* Modernize schema tables (CASSANDRA-8261)
* Support for user-defined aggregation functions (CASSANDRA-8053)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/493859bf/src/java/org/apache/cassandra/cql3/TokenRelation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/TokenRelation.java b/src/java/org/apache/cassandra/cql3/TokenRelation.java
index d1bd265..5896fae 100644
--- a/src/java/org/apache/cassandra/cql3/TokenRelation.java
+++ b/src/java/org/apache/cassandra/cql3/TokenRelation.java
@@ -69,7 +69,7 @@ public final class TokenRelation extends Relation
{
List<ColumnDefinition> columnDefs = getColumnDefinitions(cfm);
Term term = toTerm(toReceivers(cfm, columnDefs), value, cfm.ksName, boundNames);
- return new TokenRestriction.EQ(columnDefs, term);
+ return new TokenRestriction.EQ(cfm.getKeyValidatorAsCType(), columnDefs, term);
}
@Override
@@ -86,7 +86,7 @@ public final class TokenRelation extends Relation
{
List<ColumnDefinition> columnDefs = getColumnDefinitions(cfm);
Term term = toTerm(toReceivers(cfm, columnDefs), value, cfm.ksName, boundNames);
- return new TokenRestriction.Slice(columnDefs, bound, inclusive, term);
+ return new TokenRestriction.Slice(cfm.getKeyValidatorAsCType(), columnDefs, bound, inclusive, term);
}
@Override
http://git-wip-us.apache.org/repos/asf/cassandra/blob/493859bf/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
index f137a77..0107603 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
@@ -17,11 +17,23 @@
*/
package org.apache.cassandra.cql3.restrictions;
+import org.apache.cassandra.db.composites.CType;
+
/**
* Base class for <code>PrimaryKeyRestrictions</code>.
*/
abstract class AbstractPrimaryKeyRestrictions extends AbstractRestriction implements PrimaryKeyRestrictions
{
+ /**
+ * The composite type.
+ */
+ protected final CType ctype;
+
+ public AbstractPrimaryKeyRestrictions(CType ctype)
+ {
+ this.ctype = ctype;
+ }
+
@Override
public final boolean isEmpty()
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/493859bf/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
index e3b3c4c..2d6deeb 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
@@ -43,8 +43,6 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.invalidReq
public abstract class MultiColumnRestriction extends AbstractPrimaryKeyRestrictions
{
- protected final CType ctype;
-
/**
* The columns to which the restriction apply.
*/
@@ -52,7 +50,7 @@ public abstract class MultiColumnRestriction extends AbstractPrimaryKeyRestricti
public MultiColumnRestriction(CType ctype, List<ColumnDefinition> columnDefs)
{
- this.ctype = ctype;
+ super(ctype);
this.columnDefs = columnDefs;
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/493859bf/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnPrimaryKeyRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnPrimaryKeyRestrictions.java
index 5c8386e..d2a3885 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnPrimaryKeyRestrictions.java
@@ -43,11 +43,6 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
final class SingleColumnPrimaryKeyRestrictions extends AbstractPrimaryKeyRestrictions
{
/**
- * The composite type.
- */
- private final CType ctype;
-
- /**
* The restrictions.
*/
private final SingleColumnRestrictions restrictions;
@@ -74,7 +69,7 @@ final class SingleColumnPrimaryKeyRestrictions extends AbstractPrimaryKeyRestric
public SingleColumnPrimaryKeyRestrictions(CType ctype)
{
- this.ctype = ctype;
+ super(ctype);
this.restrictions = new SingleColumnRestrictions();
this.eq = true;
}
@@ -82,8 +77,8 @@ final class SingleColumnPrimaryKeyRestrictions extends AbstractPrimaryKeyRestric
private SingleColumnPrimaryKeyRestrictions(SingleColumnPrimaryKeyRestrictions primaryKeyRestrictions,
SingleColumnRestriction restriction) throws InvalidRequestException
{
+ super(primaryKeyRestrictions.ctype);
this.restrictions = primaryKeyRestrictions.restrictions.addRestriction(restriction);
- this.ctype = primaryKeyRestrictions.ctype;
if (!primaryKeyRestrictions.isEmpty())
{
@@ -166,9 +161,10 @@ final class SingleColumnPrimaryKeyRestrictions extends AbstractPrimaryKeyRestric
if (restriction.isOnToken())
{
- checkTrue(isEmpty(), "Columns \"%s\" cannot be restricted by both a normal relation and a token relation",
- ((TokenRestriction) restriction).getColumnNamesAsString());
- return (PrimaryKeyRestrictions) restriction;
+ if (isEmpty())
+ return (PrimaryKeyRestrictions) restriction;
+
+ return new TokenFilter(this, (TokenRestriction) restriction);
}
return new SingleColumnPrimaryKeyRestrictions(this, (SingleColumnRestriction) restriction);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/493859bf/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
index 60c7465..598478c 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
@@ -259,10 +259,9 @@ public final class StatementRestrictions
// If a component of the partition key is restricted by a relation, all preceding
// components must have a EQ. Only the last partition key component can be in IN relation.
if (partitionKeyRestrictions.isOnToken())
- {
isKeyRange = true;
- }
- else if (hasPartitionKeyUnrestrictedComponents())
+
+ if (hasPartitionKeyUnrestrictedComponents())
{
if (!partitionKeyRestrictions.isEmpty())
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/493859bf/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java b/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
new file mode 100644
index 0000000..4b5383b
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.collect.BoundType;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.dht.IPartitioner;
+import org.apache.cassandra.dht.Token;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.service.StorageService;
+
+import static org.apache.cassandra.cql3.statements.Bound.END;
+import static org.apache.cassandra.cql3.statements.Bound.START;
+
+/**
+ * <code>Restriction</code> decorator used to merge non-token restriction and token restriction on partition keys.
+ */
+final class TokenFilter extends ForwardingPrimaryKeyRestrictions
+{
+ /**
+ * The decorated restriction
+ */
+ private PrimaryKeyRestrictions restrictions;
+
+ /**
+ * The restriction on the token
+ */
+ private TokenRestriction tokenRestriction;
+
+ /**
+ * The partitioner
+ */
+ private static final IPartitioner partitioner = StorageService.getPartitioner();
+
+ @Override
+ protected PrimaryKeyRestrictions getDelegate()
+ {
+ return restrictions;
+ }
+
+ @Override
+ public boolean isOnToken()
+ {
+ // if all partition key columns have non-token restrictions, we can simply use the token range to filter
+ // those restrictions and then ignore the token range
+ return restrictions.size() < tokenRestriction.size();
+ }
+
+ public TokenFilter(PrimaryKeyRestrictions restrictions, TokenRestriction tokenRestriction)
+ {
+ this.restrictions = restrictions;
+ this.tokenRestriction = tokenRestriction;
+ }
+
+ @Override
+ public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
+ {
+ return filter(restrictions.values(options), options);
+ }
+
+ @Override
+ public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction.isOnToken())
+ return new TokenFilter(restrictions, (TokenRestriction) tokenRestriction.mergeWith(restriction));
+
+ return new TokenFilter(super.mergeWith(restriction), tokenRestriction);
+ }
+
+ @Override
+ public boolean isInclusive(Bound bound)
+ {
+ return tokenRestriction.isInclusive(bound);
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return tokenRestriction.hasBound(b);
+ }
+
+ @Override
+ public List<ByteBuffer> bounds(Bound bound, QueryOptions options) throws InvalidRequestException
+ {
+ return tokenRestriction.bounds(bound, options);
+ }
+
+ @Override
+ public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
+ {
+ return tokenRestriction.boundsAsComposites(bound, options);
+ }
+
+ /**
+ * Filter the values returned by the restriction.
+ *
+ * @param values the values returned by the decorated restriction
+ * @param options the query options
+ * @return the values matching the token restriction
+ * @throws InvalidRequestException if the request is invalid
+ */
+ private List<ByteBuffer> filter(List<ByteBuffer> values, QueryOptions options) throws InvalidRequestException
+ {
+ RangeSet<Token> rangeSet = tokenRestriction.isSlice() ? toRangeSet(tokenRestriction, options)
+ : toRangeSet(tokenRestriction.values(options));
+
+ return filterWithRangeSet(rangeSet, values);
+ }
+
+ /**
+ * Filter out the values for which the tokens are not included within the specified range.
+ *
+ * @param tokens the tokens range
+ * @param values the restricted values
+ * @return the values for which the tokens are not included within the specified range.
+ */
+ private static List<ByteBuffer> filterWithRangeSet(RangeSet<Token> tokens, List<ByteBuffer> values)
+ {
+ List<ByteBuffer> remaining = new ArrayList<>();
+
+ for (ByteBuffer value : values)
+ {
+ Token token = partitioner.getToken(value);
+
+ if (!tokens.contains(token))
+ continue;
+
+ remaining.add(value);
+ }
+ return remaining;
+ }
+
+ /**
+ * Converts the specified list into a range set.
+ *
+ * @param buffers the token restriction values
+ * @return the range set corresponding to the specified list
+ */
+ private static RangeSet<Token> toRangeSet(List<ByteBuffer> buffers)
+ {
+ ImmutableRangeSet.Builder<Token> builder = ImmutableRangeSet.builder();
+
+ for (ByteBuffer buffer : buffers)
+ builder.add(Range.singleton(deserializeToken(buffer)));
+
+ return builder.build();
+ }
+
+ /**
+ * Converts the specified slice into a range set.
+ *
+ * @param slice the slice to convert
+ * @param options the query option
+ * @return the range set corresponding to the specified slice
+ * @throws InvalidRequestException if the request is invalid
+ */
+ private static RangeSet<Token> toRangeSet(TokenRestriction slice, QueryOptions options) throws InvalidRequestException
+ {
+ if (slice.hasBound(START))
+ {
+ Token start = deserializeToken(slice.bounds(START, options).get(0));
+
+ BoundType startBoundType = toBoundType(slice.isInclusive(START));
+ BoundType endBoundType = toBoundType(slice.isInclusive(END));
+
+ if (slice.hasBound(END))
+ {
+ Token end = deserializeToken(slice.bounds(END, options).get(0));
+
+ if (start.equals(end) && (BoundType.OPEN == startBoundType || BoundType.OPEN == endBoundType))
+ return ImmutableRangeSet.of();
+
+ if (start.compareTo(end) <= 0)
+ return ImmutableRangeSet.of(Range.range(start,
+ startBoundType,
+ end,
+ endBoundType));
+
+ return ImmutableRangeSet.<Token> builder()
+ .add(Range.upTo(end, endBoundType))
+ .add(Range.downTo(start, startBoundType))
+ .build();
+ }
+ return ImmutableRangeSet.of(Range.downTo(start,
+ startBoundType));
+ }
+ Token end = deserializeToken(slice.bounds(END, options).get(0));
+ return ImmutableRangeSet.of(Range.upTo(end, toBoundType(slice.isInclusive(END))));
+ }
+
+ /**
+ * Deserializes the token corresponding to the specified buffer.
+ *
+ * @param buffer the buffer
+ * @return the token corresponding to the specified buffer
+ */
+ private static Token deserializeToken(ByteBuffer buffer)
+ {
+ return partitioner.getTokenFactory().fromByteArray(buffer);
+ }
+
+ private static BoundType toBoundType(boolean inclusive)
+ {
+ return inclusive ? BoundType.CLOSED : BoundType.OPEN;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/493859bf/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
index 85d614e..8d63fea 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
@@ -29,6 +29,7 @@ import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.statements.Bound;
import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CType;
import org.apache.cassandra.db.composites.Composite;
import org.apache.cassandra.db.index.SecondaryIndexManager;
import org.apache.cassandra.exceptions.InvalidRequestException;
@@ -48,10 +49,12 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
/**
* Creates a new <code>TokenRestriction</code> that apply to the specified columns.
*
+ * @param ctype the composite type
* @param columnDefs the definition of the columns to which apply the token restriction
*/
- public TokenRestriction(List<ColumnDefinition> columnDefs)
+ public TokenRestriction(CType ctype, List<ColumnDefinition> columnDefs)
{
+ super(ctype);
this.columnDefs = columnDefs;
}
@@ -101,13 +104,43 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
return Joiner.on(", ").join(ColumnDefinition.toIdentifiers(columnDefs));
}
+ @Override
+ public final PrimaryKeyRestrictions mergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ if (!otherRestriction.isOnToken())
+ return new TokenFilter(toPrimaryKeyRestriction(otherRestriction), this);
+
+ return doMergeWith((TokenRestriction) otherRestriction);
+ }
+
+ /**
+ * Merges this restriction with the specified <code>TokenRestriction</code>.
+ * @param otherRestriction the <code>TokenRestriction</code> to merge with.
+ */
+ protected abstract PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction) throws InvalidRequestException;
+
+ /**
+ * Converts the specified restriction into a <code>PrimaryKeyRestrictions</code>.
+ *
+ * @param restriction the restriction to convert
+ * @return a <code>PrimaryKeyRestrictions</code>
+ * @throws InvalidRequestException if a problem occurs while converting the restriction
+ */
+ private PrimaryKeyRestrictions toPrimaryKeyRestriction(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction instanceof PrimaryKeyRestrictions)
+ return (PrimaryKeyRestrictions) restriction;
+
+ return new SingleColumnPrimaryKeyRestrictions(ctype).mergeWith(restriction);
+ }
+
public static final class EQ extends TokenRestriction
{
private final Term value;
- public EQ(List<ColumnDefinition> columnDefs, Term value)
+ public EQ(CType ctype, List<ColumnDefinition> columnDefs, Term value)
{
- super(columnDefs);
+ super(ctype, columnDefs);
this.value = value;
}
@@ -124,7 +157,7 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
}
@Override
- public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ protected PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction) throws InvalidRequestException
{
throw invalidRequest("%s cannot be restricted by more than one relation if it includes an Equal",
Joiner.on(", ").join(ColumnDefinition.toIdentifiers(columnDefs)));
@@ -141,9 +174,9 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
{
private final TermSlice slice;
- public Slice(List<ColumnDefinition> columnDefs, Bound bound, boolean inclusive, Term term)
+ public Slice(CType ctype, List<ColumnDefinition> columnDefs, Bound bound, boolean inclusive, Term term)
{
- super(columnDefs);
+ super(ctype, columnDefs);
slice = TermSlice.newInstance(bound, inclusive, term);
}
@@ -185,13 +218,9 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
}
@Override
- public PrimaryKeyRestrictions mergeWith(Restriction otherRestriction)
+ protected PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction)
throws InvalidRequestException
{
- if (!otherRestriction.isOnToken())
- throw invalidRequest("Columns \"%s\" cannot be restricted by both a normal relation and a token relation",
- getColumnNamesAsString());
-
if (!otherRestriction.isSlice())
throw invalidRequest("Columns \"%s\" cannot be restricted by both an equality and an inequality relation",
getColumnNamesAsString());
@@ -206,7 +235,7 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
throw invalidRequest("More than one restriction was found for the end bound on %s",
getColumnNamesAsString());
- return new Slice(columnDefs, slice.merge(otherSlice.slice));
+ return new Slice(ctype, columnDefs, slice.merge(otherSlice.slice));
}
@Override
@@ -215,9 +244,9 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
return String.format("SLICE%s", slice);
}
- private Slice(List<ColumnDefinition> columnDefs, TermSlice slice)
+ private Slice(CType ctype, List<ColumnDefinition> columnDefs, TermSlice slice)
{
- super(columnDefs);
+ super(ctype, columnDefs);
this.slice = slice;
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/493859bf/test/unit/org/apache/cassandra/cql3/SelectWithTokenFunctionTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/SelectWithTokenFunctionTest.java b/test/unit/org/apache/cassandra/cql3/SelectWithTokenFunctionTest.java
index 39b62e3..a365c09 100644
--- a/test/unit/org/apache/cassandra/cql3/SelectWithTokenFunctionTest.java
+++ b/test/unit/org/apache/cassandra/cql3/SelectWithTokenFunctionTest.java
@@ -17,6 +17,8 @@
*/
package org.apache.cassandra.cql3;
+import java.util.Arrays;
+
import org.junit.Test;
public class SelectWithTokenFunctionTest extends CQLTester
@@ -30,10 +32,6 @@ public class SelectWithTokenFunctionTest extends CQLTester
assertRows(execute("SELECT * FROM %s WHERE token(a) >= token(?)", 0), row(0, "a"));
assertRows(execute("SELECT * FROM %s WHERE token(a) >= token(?) and token(a) < token(?)", 0, 1), row(0, "a"));
assertInvalid("SELECT * FROM %s WHERE token(a) > token(?)", "a");
- assertInvalidMessage("Columns \"a\" cannot be restricted by both a normal relation and a token relation",
- "SELECT * FROM %s WHERE token(a) > token(?) AND a = ?", 1, 1);
- assertInvalidMessage("Columns \"a\" cannot be restricted by both a normal relation and a token relation",
- "SELECT * FROM %s WHERE a = ? and token(a) > token(?)", 1, 1);
assertInvalidMessage("The token() function must contains only partition key components",
"SELECT * FROM %s WHERE token(a, b) >= token(?, ?)", "b", 0);
assertInvalidMessage("More than one restriction was found for the start bound on a",
@@ -88,4 +86,137 @@ public class SelectWithTokenFunctionTest extends CQLTester
assertInvalidMessage("The token() function must be applied to all partition key components or none of them",
"SELECT * FROM %s WHERE token(a) > token(?, ?) and token(b) > token(?, ?)", 0, "a", 0, "a");
}
+
+ @Test
+ public void testSingleColumnPartitionKeyWithTokenNonTokenRestrictionsMix() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int primary key, b int)");
+
+ execute("INSERT INTO %s (a, b) VALUES (0, 0);");
+ execute("INSERT INTO %s (a, b) VALUES (1, 1);");
+ execute("INSERT INTO %s (a, b) VALUES (2, 2);");
+ execute("INSERT INTO %s (a, b) VALUES (3, 3);");
+ execute("INSERT INTO %s (a, b) VALUES (4, 4);");
+ assertRows(execute("SELECT * FROM %s WHERE a IN (?, ?);", 1, 3),
+ row(1, 1),
+ row(3, 3));
+ assertRows(execute("SELECT * FROM %s WHERE token(a)> token(?) and token(a) <= token(?);", 1, 3),
+ row(2, 2),
+ row(3, 3));
+ assertRows(execute("SELECT * FROM %s WHERE token(a)= token(2);"),
+ row(2, 2));
+ assertRows(execute("SELECT * FROM %s WHERE token(a) > token(?) AND token(a) <= token(?) AND a IN (?, ?);",
+ 1, 3, 1, 3),
+ row(3, 3));
+ assertRows(execute("SELECT * FROM %s WHERE token(a) < token(?) AND token(a) >= token(?) AND a IN (?, ?);",
+ 1, 3, 1, 3),
+ row(3, 3));
+ assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
+ "SELECT * FROM %s WHERE token(a) > token(?) AND token(a) <= token(?) AND a > ?;", 1, 3, 1);
+
+ assertRows(execute("SELECT * FROM %s WHERE token(a) > token(?) AND token(a) <= token(?) AND a IN ?;",
+ 1, 3, Arrays.asList(1, 3)),
+ row(3, 3));
+ assertRows(execute("SELECT * FROM %s WHERE token(a) > token(?) AND a = ?;", 1, 3),
+ row(3, 3));
+ assertRows(execute("SELECT * FROM %s WHERE a = ? AND token(a) > token(?);", 3, 1),
+ row(3, 3));
+ assertEmpty(execute("SELECT * FROM %s WHERE token(a) > token(?) AND a = ?;", 3, 1));
+ assertEmpty(execute("SELECT * FROM %s WHERE a = ? AND token(a) > token(?);", 1, 3));
+ assertRows(execute("SELECT * FROM %s WHERE token(a) > token(?) AND a IN (?, ?);", 2, 1, 3),
+ row(3, 3));
+ assertRows(execute("SELECT * FROM %s WHERE token(a) > token(?) AND token(a) < token(?) AND a IN (?, ?) ;", 2, 5, 1, 3),
+ row(3, 3));
+ assertRows(execute("SELECT * FROM %s WHERE a IN (?, ?) AND token(a) > token(?) AND token(a) < token(?);", 1, 3, 2, 5),
+ row(3, 3));
+ assertRows(execute("SELECT * FROM %s WHERE token(a) > token(?) AND a IN (?, ?) AND token(a) < token(?);", 2, 1, 3, 5),
+ row(3, 3));
+ assertEmpty(execute("SELECT * FROM %s WHERE a IN (?, ?) AND token(a) > token(?);", 1, 3, 3));
+ assertRows(execute("SELECT * FROM %s WHERE token(a) <= token(?) AND a = ?;", 2, 2),
+ row(2, 2));
+ assertEmpty(execute("SELECT * FROM %s WHERE token(a) <= token(?) AND a = ?;", 2, 3));
+ assertEmpty(execute("SELECT * FROM %s WHERE token(a) = token(?) AND a = ?;", 2, 3));
+ assertRows(execute("SELECT * FROM %s WHERE token(a) >= token(?) AND token(a) <= token(?) AND a = ?;", 2, 2, 2),
+ row(2, 2));
+ assertEmpty(execute("SELECT * FROM %s WHERE token(a) >= token(?) AND token(a) < token(?) AND a = ?;", 2, 2, 2));
+ assertEmpty(execute("SELECT * FROM %s WHERE token(a) > token(?) AND token(a) <= token(?) AND a = ?;", 2, 2, 2));
+ assertEmpty(execute("SELECT * FROM %s WHERE token(a) > token(?) AND token(a) < token(?) AND a = ?;", 2, 2, 2));
+ }
+
+ @Test
+ public void testMultiColumnPartitionKeyWithTokenNonTokenRestrictionsMix() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int, primary key((a, b)))");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (0, 0, 0);");
+ execute("INSERT INTO %s (a, b, c) VALUES (0, 1, 1);");
+ execute("INSERT INTO %s (a, b, c) VALUES (0, 2, 2);");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 0, 3);");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 1, 4);");
+
+ assertRows(execute("SELECT * FROM %s WHERE token(a, b) > token(?, ?);", 0, 0),
+ row(0, 1, 1),
+ row(0, 2, 2),
+ row(1, 0, 3),
+ row(1, 1, 4));
+
+ assertRows(execute("SELECT * FROM %s WHERE token(a, b) > token(?, ?) AND a = ? AND b IN (?, ?);",
+ 0, 0, 1, 0, 1),
+ row(1, 0, 3),
+ row(1, 1, 4));
+
+ assertRows(execute("SELECT * FROM %s WHERE a = ? AND token(a, b) > token(?, ?) AND b IN (?, ?);",
+ 1, 0, 0, 0, 1),
+ row(1, 0, 3),
+ row(1, 1, 4));
+
+ assertRows(execute("SELECT * FROM %s WHERE b IN (?, ?) AND token(a, b) > token(?, ?) AND a = ?;",
+ 0, 1, 0, 0, 1),
+ row(1, 0, 3),
+ row(1, 1, 4));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE b IN (?, ?) AND token(a, b) > token(?, ?) AND token(a, b) < token(?, ?) AND a = ?;",
+ 0, 1, 0, 0, 0, 0, 1));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE b IN (?, ?) AND token(a, b) > token(?, ?) AND token(a, b) <= token(?, ?) AND a = ?;",
+ 0, 1, 0, 0, 0, 0, 1));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE b IN (?, ?) AND token(a, b) >= token(?, ?) AND token(a, b) < token(?, ?) AND a = ?;",
+ 0, 1, 0, 0, 0, 0, 1));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE b IN (?, ?) AND token(a, b) = token(?, ?) AND a = ?;",
+ 0, 1, 0, 0, 1));
+
+ assertInvalidMessage("Partition key parts: b must be restricted as other parts are",
+ "SELECT * FROM %s WHERE token(a, b) > token(?, ?) AND a = ?;", 0, 0, 1);
+ }
+
+ @Test
+ public void testMultiColumnPartitionKeyWithIndexAndTokenNonTokenRestrictionsMix() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int, primary key((a, b)))");
+ createIndex("CREATE INDEX ON %s(b)");
+ createIndex("CREATE INDEX ON %s(c)");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (0, 0, 0);");
+ execute("INSERT INTO %s (a, b, c) VALUES (0, 1, 1);");
+ execute("INSERT INTO %s (a, b, c) VALUES (0, 2, 2);");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 0, 3);");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 1, 4);");
+
+ assertRows(execute("SELECT * FROM %s WHERE b = ?;", 1),
+ row(0, 1, 1),
+ row(1, 1, 4));
+
+ assertRows(execute("SELECT * FROM %s WHERE token(a, b) > token(?, ?) AND b = ?;", 0, 0, 1),
+ row(0, 1, 1),
+ row(1, 1, 4));
+
+ assertRows(execute("SELECT * FROM %s WHERE b = ? AND token(a, b) > token(?, ?);", 1, 0, 0),
+ row(0, 1, 1),
+ row(1, 1, 4));
+
+ assertRows(execute("SELECT * FROM %s WHERE b = ? AND token(a, b) > token(?, ?) and c = ? ALLOW FILTERING;", 1, 0, 0, 4),
+ row(1, 1, 4));
+ }
}