mirror of https://github.com/apache/cassandra
BETWEEN where token(Y) > token(Z) returns wrong answer
patch by Arvind Kandpal; reviewed by Caleb Rackliffe, David Capwell for CASSANDRA-20154
This commit is contained in:
parent
3885395752
commit
bad0a10343
|
|
@ -1,4 +1,5 @@
|
|||
5.1
|
||||
* BETWEEN where token(Y) > token(Z) returns wrong answer (CASSANDRA-20154)
|
||||
* Optimize memtable flush logic (CASSANDRA-21083)
|
||||
* No need to evict already prepared statements, as it creates a race condition between multiple threads (CASSANDRA-17401)
|
||||
* Include Level information for UnifiedCompactionStrategy in nodetool tablestats output (CASSANDRA-20820)
|
||||
|
|
|
|||
|
|
@ -788,8 +788,8 @@ public enum Operator
|
|||
public boolean isSatisfiedBy(AbstractType<?> type, ByteBuffer leftOperand, ByteBuffer rightOperand)
|
||||
{
|
||||
List<ByteBuffer> buffers = ListType.getInstance(type, false).unpack(rightOperand);
|
||||
// We use compare instead of compareForCQL to deal properly with reversed clustering columns
|
||||
return type.compare(leftOperand, buffers.get(0)) >= 0 && type.compare(leftOperand, buffers.get(1)) <= 0;
|
||||
AbstractType<?> unwrapped = type.unwrap();
|
||||
return unwrapped.compare(leftOperand, buffers.get(0)) >= 0 && unwrapped.compare(leftOperand, buffers.get(1)) <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -802,11 +802,6 @@ public enum Operator
|
|||
public void restrict(RangeSet<ClusteringElements> rangeSet, List<ClusteringElements> args, IPartitioner partitioner)
|
||||
{
|
||||
assert args.size() == 2 : this + " accepts exactly two values";
|
||||
// avoid sorting when working with token restrictions, otherwise we can't know the difference between these queries:
|
||||
// select * from x.y where token(id) between 0 and MIN_TOKEN
|
||||
// select * from x.y where token(id) between MIN_TOKEN and 0
|
||||
if (!args.get(0).token)
|
||||
args.sort(ClusteringElements.CQL_COMPARATOR);
|
||||
rangeSet.removeAll(ClusteringElements.lessThan(args.get(0), partitioner));
|
||||
rangeSet.removeAll(ClusteringElements.greaterThan(args.get(1), partitioner));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -343,8 +343,7 @@ public final class SimpleRestriction implements SingleRestriction
|
|||
List<ByteBuffer> buffers = bindAndGet(options);
|
||||
if (operator.kind() != Operator.Kind.BINARY)
|
||||
{
|
||||
// For BETWEEN we support like in SQL reversed bounds
|
||||
if (operator.kind() == Operator.Kind.TERNARY)
|
||||
if (operator == Operator.IN && !column.type.isCounter())
|
||||
buffers.sort(column.type);
|
||||
filter.add(column, operator, multiInputOperatorValues(column, buffers));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,8 +246,7 @@ public abstract class Expression
|
|||
Value first = new Value(buffers.get(0), indexTermType);
|
||||
Value second = new Value(buffers.get(1), indexTermType);
|
||||
|
||||
// SimpleRestriction#addToRowFilter() ensures correct bounds ordering, but SAI enforces a non-arbitrary
|
||||
// ordering between IPv4 and IPv6 addresses, so correction may still be necessary.
|
||||
// SAI enforces a non-arbitrary ordering between IPv4 and IPv6 addresses, so correction may still be necessary.
|
||||
boolean outOfOrder = indexTermType.compare(first.encoded, second.encoded) > 0;
|
||||
lower = new Bound(outOfOrder ? second : first, true);
|
||||
upper = new Bound(outOfOrder ? first : second, true);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.distributed.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.distributed.Cluster;
|
||||
import org.apache.cassandra.distributed.api.ConsistencyLevel;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests that the BETWEEN operator respects SQL semantics:
|
||||
* - Normal BETWEEN (low <= high) returns rows.
|
||||
* - Inverted BETWEEN (low > high) returns no rows (empty result).
|
||||
*/
|
||||
public class BetweenInversionTest extends TestBaseImpl
|
||||
{
|
||||
@Test
|
||||
public void testBetweenInversion() throws Throwable
|
||||
{
|
||||
try (Cluster cluster = init(Cluster.build(1).start()))
|
||||
{
|
||||
cluster.schemaChange("CREATE KEYSPACE ks WITH replication = {'class':'SimpleStrategy','replication_factor':1}");
|
||||
cluster.schemaChange("CREATE TABLE ks.t1 (pk int PRIMARY KEY, val text)");
|
||||
|
||||
cluster.coordinator(1).execute("INSERT INTO ks.t1 (pk,val) VALUES (1,'a')", ConsistencyLevel.ALL);
|
||||
cluster.coordinator(1).execute("INSERT INTO ks.t1 (pk,val) VALUES (2,'b')", ConsistencyLevel.ALL);
|
||||
|
||||
Object[][] rows = cluster.coordinator(1)
|
||||
.execute("SELECT * FROM ks.t1 WHERE pk BETWEEN 1 AND 2 ALLOW FILTERING", ConsistencyLevel.ALL);
|
||||
assertEquals(2, rows.length);
|
||||
|
||||
Object[][] inverted = cluster.coordinator(1)
|
||||
.execute("SELECT * FROM ks.t1 WHERE pk BETWEEN 2 AND 1 ALLOW FILTERING", ConsistencyLevel.ALL);
|
||||
assertEquals(0, inverted.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -349,6 +349,86 @@ public class SingleNodeTableWalkTest extends StatefulASTBase
|
|||
return state.command(rs, select, symbol.detailedName() + (indexed == null ? "" : ", indexed with " + indexed.indexDDL.indexer.name()));
|
||||
}
|
||||
|
||||
public Property.Command<State, Void, ?> clusteringBetweenQuery(RandomSource rs, State state)
|
||||
{
|
||||
ImmutableUniqueList<Symbol> cks = state.model.factory.clusteringColumns;
|
||||
if (cks.isEmpty())
|
||||
return Property.ignoreCommand();
|
||||
|
||||
// Select a single partition
|
||||
BytesPartitionState.Ref ref = rs.pickOrderedSet(state.model.partitionKeys());
|
||||
BytesPartitionState partition = state.model.get(ref);
|
||||
|
||||
NavigableSet<Clustering<ByteBuffer>> clusteringKeys = partition.clusteringKeys();
|
||||
|
||||
// Use first clustering column for BETWEEN
|
||||
Symbol ckSymbol = cks.get(0);
|
||||
|
||||
ByteBuffer low, high;
|
||||
boolean useRealValues = clusteringKeys.size() >= 2 && rs.nextBoolean();
|
||||
if (useRealValues)
|
||||
{
|
||||
// low and high can match, have high < low, and low < high... this is all expected
|
||||
// between where low > high should return nothing
|
||||
// between where low == high should be the same as a ck=? query
|
||||
low = rs.pickOrderedSet(clusteringKeys).bufferAt(0);
|
||||
high = rs.pickOrderedSet(clusteringKeys).bufferAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate random values
|
||||
Gen<ByteBuffer> bytesGen = toGen(getTypeSupport(ckSymbol.type()).bytesGen());
|
||||
low = bytesGen.next(rs);
|
||||
high = bytesGen.next(rs);
|
||||
}
|
||||
|
||||
Select.Builder builder = Select.builder().table(state.metadata);
|
||||
|
||||
// Add partition key restrictions
|
||||
ImmutableUniqueList<Symbol> pks = state.model.factory.partitionColumns;
|
||||
Clustering<ByteBuffer> pkKey = ref.key;
|
||||
for (Symbol pk : pks)
|
||||
builder.value(pk, pkKey.bufferAt(pks.indexOf(pk)));
|
||||
|
||||
|
||||
String annotation;
|
||||
if (rs.nextBoolean())
|
||||
{
|
||||
builder.between(ckSymbol, state.value(rs, low, ckSymbol.type()), state.value(rs, high, ckSymbol.type()));
|
||||
annotation = "clustering BETWEEN";
|
||||
}
|
||||
else if (rs.nextBoolean())
|
||||
{
|
||||
builder.where(ckSymbol, Inequality.GREATER_THAN_EQ, low);
|
||||
builder.where(ckSymbol, Inequality.LESS_THAN_EQ, high);
|
||||
annotation = "clustering >= AND <=";
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.where(ckSymbol, Inequality.GREATER_THAN, low);
|
||||
builder.where(ckSymbol, Inequality.LESS_THAN, high);
|
||||
annotation = "clustering > AND <";
|
||||
}
|
||||
|
||||
if (rs.nextBoolean())
|
||||
builder.orderByColumn(ckSymbol, rs.pick(Select.OrderBy.Ordering.values()));
|
||||
|
||||
// Check if clustering column is indexed
|
||||
var indexed = state.indexes.get(ckSymbol);
|
||||
if (indexed != null)
|
||||
annotation += " (indexed with " + indexed.indexDDL.indexer.name() + ')';
|
||||
|
||||
// Check if column is reversed (DESC)
|
||||
if (ckSymbol.reversed)
|
||||
annotation += " [DESC]";
|
||||
|
||||
if (!useRealValues)
|
||||
annotation += " [random values]";
|
||||
|
||||
Select select = builder.build();
|
||||
return state.command(rs, select, annotation);
|
||||
}
|
||||
|
||||
protected State createState(RandomSource rs, Cluster cluster)
|
||||
{
|
||||
return new State(rs, cluster);
|
||||
|
|
@ -383,6 +463,7 @@ public class SingleNodeTableWalkTest extends StatefulASTBase
|
|||
.addIf(State::allowNonPartitionQuery, this::nonPartitionQuery)
|
||||
.addIf(State::allowNonPartitionMultiColumnQuery, this::multiColumnQuery)
|
||||
.addIf(State::allowPartitionQuery, this::partitionRestrictedQuery)
|
||||
.addIf(State::allowClusteringBetweenQuery, this::clusteringBetweenQuery)
|
||||
.destroyState(State::close)
|
||||
.commandsTransformer(LoggingCommand.factory())
|
||||
.onSuccess(onSuccess(logger))
|
||||
|
|
@ -608,6 +689,11 @@ public class SingleNodeTableWalkTest extends StatefulASTBase
|
|||
return !(model.isEmpty() || searchableNonPartitionColumns.isEmpty());
|
||||
}
|
||||
|
||||
public boolean allowClusteringBetweenQuery()
|
||||
{
|
||||
return hasPartitions() && !model.factory.clusteringColumns.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ import accord.utils.Property;
|
|||
import accord.utils.RandomSource;
|
||||
|
||||
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||
import org.apache.cassandra.cql3.KnownIssue;
|
||||
import org.apache.cassandra.cql3.ast.Conditional.Where.Inequality;
|
||||
import org.apache.cassandra.cql3.ast.FunctionCall;
|
||||
import org.apache.cassandra.cql3.ast.Mutation;
|
||||
|
|
@ -134,13 +133,6 @@ public class SingleNodeTokenConflictTest extends StatefulASTBase
|
|||
ByteBuffer left = state.pkGen.next(rs);
|
||||
ByteBuffer right = state.betweenEqGen.next(rs) ? left : state.pkGen.next(rs);
|
||||
int rc = PK_TYPE.compare(left, right);
|
||||
if (rc > 0 && IGNORED_ISSUES.contains(KnownIssue.BETWEEN_START_LARGER_THAN_END))
|
||||
{
|
||||
ByteBuffer tmp = left;
|
||||
left = right;
|
||||
right = tmp;
|
||||
rc = PK_TYPE.compare(left, right);
|
||||
}
|
||||
Select select = Select.builder()
|
||||
.table(state.tableRef)
|
||||
.between(PK, state.pkValue(rs, left), state.pkValue(rs, right))
|
||||
|
|
@ -205,16 +197,6 @@ public class SingleNodeTokenConflictTest extends StatefulASTBase
|
|||
LongToken start = Murmur3Partitioner.instance.getToken(left);
|
||||
LongToken end = Murmur3Partitioner.instance.getToken(right);
|
||||
int rc = start.compareTo(end);
|
||||
if (rc > 0 && IGNORED_ISSUES.contains(KnownIssue.BETWEEN_START_LARGER_THAN_END))
|
||||
{
|
||||
ByteBuffer tmp = left;
|
||||
left = right;
|
||||
right = tmp;
|
||||
LongToken tmp2 = start;
|
||||
start = end;
|
||||
end = tmp2;
|
||||
rc = start.compareTo(end);
|
||||
}
|
||||
Select select = Select.builder()
|
||||
.table(state.tableRef)
|
||||
.between(FunctionCall.tokenByColumns(PK),
|
||||
|
|
|
|||
|
|
@ -371,13 +371,6 @@ public class StatefulASTBase extends TestBaseImpl
|
|||
}
|
||||
else
|
||||
{
|
||||
// it's possible that the range was flipped, which is known bug with BETWEEN, so
|
||||
// make sure the range is not flipped until that bug is fixed
|
||||
if (IGNORED_ISSUES.contains(KnownIssue.BETWEEN_START_LARGER_THAN_END))
|
||||
{
|
||||
min = Literal.of(key.token.getLongValue());
|
||||
max = Literal.of(Long.MIN_VALUE);
|
||||
}
|
||||
select = Select.builder(state.metadata)
|
||||
.between(tokenCall, min, max)
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import java.util.stream.Stream;
|
|||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
|
|
@ -1580,7 +1581,7 @@ public class ASTSingleTableModel
|
|||
ImmutableUniqueList<Symbol> selectOrder = factory.selectionOrder;
|
||||
ImmutableUniqueList<Symbol> targetOrder = columns(select);
|
||||
if (select.where.isEmpty())
|
||||
return SelectResult.ordered(targetOrder, filter(getRowsAsByteBuffer(applyLimits(all(), select.perPartitionLimit, select.limit)), selectOrder, targetOrder));
|
||||
return SelectResult.ordered(targetOrder, filter(getRowsAsByteBuffer(applyLimits(all(), select.perPartitionLimit, select.limit, select.orderBy)), selectOrder, targetOrder));
|
||||
LookupContext ctx = context(select);
|
||||
List<PrimaryKey> primaryKeys;
|
||||
if (ctx.unmatchable)
|
||||
|
|
@ -1606,13 +1607,17 @@ public class ASTSingleTableModel
|
|||
// partial tested (handles many columns, tests are single column)
|
||||
primaryKeys = search(ctx);
|
||||
}
|
||||
primaryKeys = applyLimits(primaryKeys, select.perPartitionLimit, select.limit);
|
||||
primaryKeys = applyLimits(primaryKeys, select.perPartitionLimit, select.limit, select.orderBy);
|
||||
//TODO (correctness): now that we have the rows we need to handle the selections/aggregation/limit/group-by/etc.
|
||||
return new SelectResult(targetOrder, filter(getRowsAsByteBuffer(primaryKeys), selectOrder, targetOrder), ctx.unordered);
|
||||
}
|
||||
|
||||
private List<PrimaryKey> applyLimits(List<PrimaryKey> primaryKeys, Optional<Value> perPartitionLimitOpt, Optional<Value> limitOpt)
|
||||
private List<PrimaryKey> applyLimits(List<PrimaryKey> primaryKeys,
|
||||
Optional<Value> perPartitionLimitOpt, Optional<Value> limitOpt,
|
||||
Optional<Select.OrderBy> orderBy)
|
||||
{
|
||||
if (orderBy.isPresent() && shouldReverse(orderBy.get()))
|
||||
primaryKeys = Lists.reverse(primaryKeys);
|
||||
if (perPartitionLimitOpt.isPresent())
|
||||
{
|
||||
int limit = Int32Type.instance.compose(eval(perPartitionLimitOpt.get()));
|
||||
|
|
@ -1640,6 +1645,18 @@ public class ASTSingleTableModel
|
|||
return primaryKeys;
|
||||
}
|
||||
|
||||
private boolean shouldReverse(Select.OrderBy orderBy)
|
||||
{
|
||||
for (var block : orderBy.ordered)
|
||||
{
|
||||
Symbol col = (Symbol) block.expression; //TODO (coverage): do we support anything other than symbol?
|
||||
col = factory.clusteringColumns.get(col); // switch to table symbol so we know if its reversed or not
|
||||
if (col.reversed != (block.ordering == Select.OrderBy.Ordering.DESC))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<PrimaryKey> all()
|
||||
{
|
||||
List<PrimaryKey> primaryKeys = new ArrayList<>();
|
||||
|
|
|
|||
|
|
@ -2698,13 +2698,13 @@ public abstract class CQLTester
|
|||
return rows;
|
||||
}
|
||||
|
||||
protected void assertEmpty(UntypedResultSet result) throws Throwable
|
||||
protected void assertEmpty(UntypedResultSet result)
|
||||
{
|
||||
if (result != null && !result.isEmpty())
|
||||
throw new AssertionError(String.format("Expected empty result but got %d rows: %s \n", result.size(), makeRowStrings(result)));
|
||||
}
|
||||
|
||||
protected void assertInvalid(String query, Object... values) throws Throwable
|
||||
protected void assertInvalid(String query, Object... values)
|
||||
{
|
||||
assertInvalidMessage(null, query, values);
|
||||
}
|
||||
|
|
@ -2778,7 +2778,7 @@ public abstract class CQLTester
|
|||
: replaceValues(query, values);
|
||||
}
|
||||
|
||||
protected void assertValidSyntax(String query) throws Throwable
|
||||
protected void assertValidSyntax(String query)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -2791,12 +2791,12 @@ public abstract class CQLTester
|
|||
}
|
||||
}
|
||||
|
||||
protected void assertInvalidSyntax(String query, Object... values) throws Throwable
|
||||
protected void assertInvalidSyntax(String query, Object... values)
|
||||
{
|
||||
assertInvalidSyntaxMessage(null, query, values);
|
||||
}
|
||||
|
||||
protected void assertInvalidSyntaxMessage(String errorMessage, String query, Object... values) throws Throwable
|
||||
protected void assertInvalidSyntaxMessage(String errorMessage, String query, Object... values)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ import java.util.EnumSet;
|
|||
*/
|
||||
public enum KnownIssue
|
||||
{
|
||||
BETWEEN_START_LARGER_THAN_END("https://issues.apache.org/jira/browse/CASSANDRA-20154",
|
||||
"BETWEEN is matching values when start > end, which should never return anything"),
|
||||
SAI_INET_MIXED("https://issues.apache.org/jira/browse/CASSANDRA-19492",
|
||||
"SAI converts ipv4 to ipv6 to simplify the index, this causes issues with range search as it starts to mix the values, which isn't always desirable or intuative"),
|
||||
CUSTOM_INDEX_MAX_COLUMN_48("https://issues.apache.org/jira/browse/CASSANDRA-19897",
|
||||
|
|
|
|||
|
|
@ -417,7 +417,12 @@ FROM [keyspace_name.] table_name
|
|||
|
||||
public T orderByColumn(String name, AbstractType<?> type, OrderBy.Ordering ordering)
|
||||
{
|
||||
orderBy.add(new Symbol(name, type), ordering);
|
||||
return orderByColumn(new Symbol(name, type), ordering);
|
||||
}
|
||||
|
||||
public T orderByColumn(Symbol column, OrderBy.Ordering ordering)
|
||||
{
|
||||
orderBy.add(column, ordering);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
private static final ByteBuffer TOO_BIG = ByteBuffer.allocate(1024 * 65);
|
||||
|
||||
@Test
|
||||
public void testSingleClusteringInvalidQueries() throws Throwable
|
||||
public void testSingleClusteringInvalidQueries()
|
||||
{
|
||||
createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a, b))");
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMultiClusteringInvalidQueries() throws Throwable
|
||||
public void testMultiClusteringInvalidQueries()
|
||||
{
|
||||
createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c, d))");
|
||||
|
||||
|
|
@ -229,9 +229,8 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
row(0, 0, 0, 0),
|
||||
row(0, 0, 1, 0));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) BETWEEN (?, ?, ?) AND (?, ?, ?)", 0, 0, 1, 0, 0, 0, 0),
|
||||
row(0, 0, 0, 0),
|
||||
row(0, 0, 1, 0));
|
||||
// BETWEEN with inverted bounds returns empty result (first bound > second bound)
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) BETWEEN (?, ?, ?) AND (?, ?, ?)", 0, 0, 1, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -317,7 +316,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSinglePartitionInvalidQueries() throws Throwable
|
||||
public void testSinglePartitionInvalidQueries()
|
||||
{
|
||||
createTable("CREATE TABLE %s (a int PRIMARY KEY, b int)");
|
||||
assertInvalidMessage("Multi-column relations can only be applied to clustering columns but was applied to: a",
|
||||
|
|
@ -329,7 +328,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSingleClustering() throws Throwable
|
||||
public void testSingleClustering()
|
||||
{
|
||||
createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a, b))");
|
||||
|
||||
|
|
@ -481,7 +480,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleClustering() throws Throwable
|
||||
public void testMultipleClustering()
|
||||
{
|
||||
createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c, d))");
|
||||
|
||||
|
|
@ -726,11 +725,8 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
row(0, 0, 0, 0)
|
||||
);
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c) BETWEEN (?, ?) AND (?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 0, 0),
|
||||
row(0, 0, 1, 1),
|
||||
row(0, 0, 1, 0),
|
||||
row(0, 0, 0, 0)
|
||||
);
|
||||
// BETWEEN with inverted bounds returns empty result (first bound > second bound)
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE a = ? AND (b, c) BETWEEN (?, ?) AND (?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 0, 0));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) < (?, ?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 1),
|
||||
row(0, 0, 1, 0),
|
||||
|
|
@ -743,11 +739,8 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
row(0, 0, 0, 0)
|
||||
);
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) BETWEEN (?, ?, ?) AND (?, ?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 1, 0, 0, 0),
|
||||
row(0, 0, 1, 1),
|
||||
row(0, 0, 1, 0),
|
||||
row(0, 0, 0, 0)
|
||||
);
|
||||
// BETWEEN with inverted bounds returns empty result (first bound > second bound)
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) BETWEEN (?, ?, ?) AND (?, ?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 1, 0, 0, 0));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) > (?, ?, ?) AND (b) < (?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 0, 1),
|
||||
row(0, 0, 1, 1)
|
||||
|
|
@ -952,7 +945,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleClusteringWithIndex() throws Throwable
|
||||
public void testMultipleClusteringWithIndex()
|
||||
{
|
||||
Util.assumeLegacySecondaryIndex();
|
||||
createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, PRIMARY KEY (a, b, c, d))");
|
||||
|
|
@ -1042,7 +1035,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleClusteringWithIndexAndValueOver64K() throws Throwable
|
||||
public void testMultipleClusteringWithIndexAndValueOver64K()
|
||||
{
|
||||
Util.assumeLegacySecondaryIndex();
|
||||
createTable("CREATE TABLE %s (a int, b blob, c int, d int, PRIMARY KEY (a, b, c))");
|
||||
|
|
@ -1056,7 +1049,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMultiColumnRestrictionsWithIndex() throws Throwable
|
||||
public void testMultiColumnRestrictionsWithIndex()
|
||||
{
|
||||
createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, v int, PRIMARY KEY (a, b, c, d, e))");
|
||||
createIndex("CREATE INDEX ON %s (v)");
|
||||
|
|
@ -1085,7 +1078,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMultiplePartitionKeyAndMultiClusteringWithIndex() throws Throwable
|
||||
public void testMultiplePartitionKeyAndMultiClusteringWithIndex()
|
||||
{
|
||||
Util.assumeLegacySecondaryIndex();
|
||||
createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, f int, PRIMARY KEY ((a, b), c, d, e))");
|
||||
|
|
@ -1229,7 +1222,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testWithUnsetValues() throws Throwable
|
||||
public void testWithUnsetValues()
|
||||
{
|
||||
createTable("CREATE TABLE %s (k int, i int, j int, s text, PRIMARY KEY(k,i,j))");
|
||||
createIndex("CREATE INDEX s_index ON %s (s)");
|
||||
|
|
@ -1330,32 +1323,12 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
row(0, -1, 0, -1, 0)
|
||||
);
|
||||
|
||||
assertRows(execute(
|
||||
// BETWEEN with inverted bounds returns empty result (first bound > second bound in logical order)
|
||||
assertEmpty(execute(
|
||||
"SELECT * FROM %s" +
|
||||
" WHERE a = ? " +
|
||||
"AND (b,c,d,e) BETWEEN (?,?,?,?) " +
|
||||
"AND (?,?,?,?)", 0, 2, 0, 1, 1, -1, -1, -1, -1),
|
||||
|
||||
row(0, 2, 0, 1, 1),
|
||||
row(0, 2, 0, -1, 0),
|
||||
row(0, 2, 0, -1, 1),
|
||||
row(0, 1, -1, 1, 0),
|
||||
row(0, 1, -1, 1, 1),
|
||||
row(0, 1, -1, 0, 0),
|
||||
row(0, 1, 0, 1, -1),
|
||||
row(0, 1, 0, 1, 1),
|
||||
row(0, 1, 0, 0, -1),
|
||||
row(0, 1, 0, 0, 0),
|
||||
row(0, 1, 0, 0, 1),
|
||||
row(0, 1, 0, -1, -1),
|
||||
row(0, 1, 1, 0, -1),
|
||||
row(0, 1, 1, 0, 0),
|
||||
row(0, 1, 1, 0, 1),
|
||||
row(0, 1, 1, -1, 0),
|
||||
row(0, 0, 0, 0, 0),
|
||||
row(0, -1, 0, 0, 0),
|
||||
row(0, -1, 0, -1, 0)
|
||||
);
|
||||
"AND (?,?,?,?)", 0, 2, 0, 1, 1, -1, -1, -1, -1));
|
||||
|
||||
assertRows(execute(
|
||||
"SELECT * FROM %s" +
|
||||
|
|
@ -1665,17 +1638,8 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
row(0, -1, 0, -1, 0)
|
||||
);
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) BETWEEN (?,?,?,?) AND (?,?,?,?)", 0, 1, 0, 0, 0, -10, -1, -4, -1),
|
||||
row(0, 1, -1, 1, 0),
|
||||
row(0, 1, -1, 1, 1),
|
||||
row(0, 1, -1, 0, 0),
|
||||
row(0, 1, 0, 0, -1),
|
||||
row(0, 1, 0, 0, 0),
|
||||
row(0, 1, 0, -1, -1),
|
||||
row(0, 0, 0, 0, 0),
|
||||
row(0, -1, 0, 0, 0),
|
||||
row(0, -1, 0, -1, 0)
|
||||
);
|
||||
// BETWEEN with inverted bounds returns empty result (first bound > second bound in logical order)
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) BETWEEN (?,?,?,?) AND (?,?,?,?)", 0, 1, 0, 0, 0, -10, -1, -4, -1));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) > (?,?,?,?)", 0, 1, 0, 0, 0),
|
||||
row(0, 2, 0, 1, 1),
|
||||
|
|
@ -2032,34 +1996,12 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
row(0, 2, -3, 1, 1)
|
||||
);
|
||||
|
||||
assertRows(execute(
|
||||
// BETWEEN with inverted bounds returns empty result (first bound > second bound in logical order)
|
||||
assertEmpty(execute(
|
||||
"SELECT * FROM %s" +
|
||||
" WHERE a = ? " +
|
||||
"AND (b,c,d,e) BETWEEN (?,?,?,?) " +
|
||||
"AND (?,?,?,?)", 0, 2, 0, 1, 1, -1, -10, -10, -10),
|
||||
|
||||
row(0, -1, 0, 0, 0),
|
||||
row(0, -1, 0, -1, 0),
|
||||
row(0, 0, 0, 0, 0),
|
||||
row(0, 1, 1, 0, -1),
|
||||
row(0, 1, 1, 0, 0),
|
||||
row(0, 1, 1, 0, 1),
|
||||
row(0, 1, 1, -1, 0),
|
||||
row(0, 1, 0, 1, -1),
|
||||
row(0, 1, 0, 1, 1),
|
||||
row(0, 1, 0, 0, -1),
|
||||
row(0, 1, 0, 0, 0),
|
||||
row(0, 1, 0, 0, 1),
|
||||
row(0, 1, 0, -1, -1),
|
||||
row(0, 1, -1, 1, 0),
|
||||
row(0, 1, -1, 1, 1),
|
||||
row(0, 1, -1, 0, 0),
|
||||
row(0, 2, 0, 1, 1),
|
||||
row(0, 2, 0, -1, 0),
|
||||
row(0, 2, 0, -1, 1),
|
||||
row(0, 2, -1, 1, 1),
|
||||
row(0, 2, -3, 1, 1)
|
||||
);
|
||||
"AND (?,?,?,?)", 0, 2, 0, 1, 1, -1, -10, -10, -10));
|
||||
|
||||
assertRows(execute(
|
||||
"SELECT * FROM %s" +
|
||||
|
|
@ -2257,7 +2199,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidColumnNames() throws Throwable
|
||||
public void testInvalidColumnNames()
|
||||
{
|
||||
createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c))");
|
||||
assertInvalidMessage("Undefined column name e", "SELECT * FROM %s WHERE (b, e) = (0, 0)");
|
||||
|
|
@ -2269,7 +2211,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testInRestrictionsWithAllowFiltering() throws Throwable
|
||||
public void testInRestrictionsWithAllowFiltering()
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk int, c1 text, c2 int, c3 int, v int, primary key(pk, c1, c2, c3))");
|
||||
execute("INSERT INTO %s (pk, c1, c2, c3, v) values (?, ?, ?, ?, ?)", 1, "0", 0, 1, 3);
|
||||
|
|
@ -2291,7 +2233,7 @@ public class SelectMultiColumnRelationTest extends CQLTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testInRestrictionsWithIndex() throws Throwable
|
||||
public void testInRestrictionsWithIndex()
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk int, c1 text, c2 int, c3 int, v int, primary key(pk, c1, c2, c3))");
|
||||
createIndex("CREATE INDEX ON %s (c3)");
|
||||
|
|
|
|||
|
|
@ -586,11 +586,9 @@ public class SelectSingleColumnRelationTest extends CQLTester
|
|||
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
|
||||
"SELECT * FROM %s WHERE a = ? AND c = ? AND d BETWEEN ? AND ? AND f = ?", 0, 1, 1, 5, 0);
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND c = ? AND d BETWEEN ? AND ? AND f = ?", 0, 0, 1, 1, 0, 5),
|
||||
row(0, 0, 1, 1, 1, 5));
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND c = ? AND d BETWEEN ? AND ? AND f = ?", 0, 0, 1, 1, 0, 5));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE a = ? AND c = ? AND d BETWEEN ? AND ? AND f = ? ALLOW FILTERING", 0, 1, 1, 0, 5),
|
||||
row(0, 0, 1, 1, 1, 5));
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE a = ? AND c = ? AND d BETWEEN ? AND ? AND f = ? ALLOW FILTERING", 0, 1, 1, 0, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1352,9 +1350,7 @@ public class SelectSingleColumnRelationTest extends CQLTester
|
|||
row (0, 2, 2, "MA"),
|
||||
row (0, 3, 3, "MA"));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE c2 BETWEEN 3 AND 2 ALLOW FILTERING"),
|
||||
row (0, 2, 2, "MA"),
|
||||
row (0, 3, 3, "MA"));
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE c2 BETWEEN 3 AND 2 ALLOW FILTERING"));
|
||||
});
|
||||
|
||||
createTable("CREATE TABLE %s(p int, c int, c2 int, abbreviation ascii, PRIMARY KEY (p, c, c2)) WITH CLUSTERING ORDER BY (c DESC, c2 DESC)");
|
||||
|
|
@ -1369,9 +1365,7 @@ public class SelectSingleColumnRelationTest extends CQLTester
|
|||
row(0, 3, 3, "MA"),
|
||||
row(0, 2, 2, "MA"));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s WHERE c2 BETWEEN 3 AND 2 ALLOW FILTERING"),
|
||||
row(0, 3, 3, "MA"),
|
||||
row(0, 2, 2, "MA"));
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE c2 BETWEEN 3 AND 2 ALLOW FILTERING"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,13 @@ public class ImmutableUniqueList<T> extends AbstractList<T> implements UniqueLis
|
|||
return values[index];
|
||||
}
|
||||
|
||||
public T get(T value)
|
||||
{
|
||||
int idx = indexOf(value);
|
||||
if (idx == -1) throw new IllegalArgumentException("Unable to find value " + value);
|
||||
return get(idx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue