This is an automated email from the ASF dual-hosted git repository.
dcapwell pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new bad0a10343 BETWEEN where token(Y) > token(Z) returns wrong answer
bad0a10343 is described below
commit bad0a103437e745ab179cc26c3f27c5acd5ef3ba
Author: Arvind Kandpal <[email protected]>
AuthorDate: Tue Jan 20 12:31:36 2026 -0800
BETWEEN where token(Y) > token(Z) returns wrong answer
patch by Arvind Kandpal; reviewed by Caleb Rackliffe, David Capwell for
CASSANDRA-20154
---
CHANGES.txt | 1 +
src/java/org/apache/cassandra/cql3/Operator.java | 9 +-
.../cql3/restrictions/SimpleRestriction.java | 3 +-
.../cassandra/index/sai/plan/Expression.java | 3 +-
.../distributed/test/BetweenInversionTest.java | 54 ++++++++++
.../test/cql3/SingleNodeTableWalkTest.java | 86 ++++++++++++++++
.../test/cql3/SingleNodeTokenConflictTest.java | 18 ----
.../distributed/test/cql3/StatefulASTBase.java | 7 --
.../cassandra/harry/model/ASTSingleTableModel.java | 23 ++++-
test/unit/org/apache/cassandra/cql3/CQLTester.java | 10 +-
.../unit/org/apache/cassandra/cql3/KnownIssue.java | 2 -
.../unit/org/apache/cassandra/cql3/ast/Select.java | 7 +-
.../operations/SelectMultiColumnRelationTest.java | 112 +++++----------------
.../operations/SelectSingleColumnRelationTest.java | 14 +--
.../cassandra/utils/ImmutableUniqueList.java | 7 ++
15 files changed, 214 insertions(+), 142 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index d2fc1ba15f..3adaa9be91 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -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)
diff --git a/src/java/org/apache/cassandra/cql3/Operator.java
b/src/java/org/apache/cassandra/cql3/Operator.java
index 7661bb7390..b9393054fc 100644
--- a/src/java/org/apache/cassandra/cql3/Operator.java
+++ b/src/java/org/apache/cassandra/cql3/Operator.java
@@ -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));
}
diff --git
a/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java
b/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java
index 310a82849c..16141b8b8c 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java
@@ -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));
}
diff --git a/src/java/org/apache/cassandra/index/sai/plan/Expression.java
b/src/java/org/apache/cassandra/index/sai/plan/Expression.java
index a5cf07e35d..27b791ab87 100644
--- a/src/java/org/apache/cassandra/index/sai/plan/Expression.java
+++ b/src/java/org/apache/cassandra/index/sai/plan/Expression.java
@@ -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);
diff --git
a/test/distributed/org/apache/cassandra/distributed/test/BetweenInversionTest.java
b/test/distributed/org/apache/cassandra/distributed/test/BetweenInversionTest.java
new file mode 100644
index 0000000000..fd89bea1b6
--- /dev/null
+++
b/test/distributed/org/apache/cassandra/distributed/test/BetweenInversionTest.java
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git
a/test/distributed/org/apache/cassandra/distributed/test/cql3/SingleNodeTableWalkTest.java
b/test/distributed/org/apache/cassandra/distributed/test/cql3/SingleNodeTableWalkTest.java
index 938065467e..6c7c58936c 100644
---
a/test/distributed/org/apache/cassandra/distributed/test/cql3/SingleNodeTableWalkTest.java
+++
b/test/distributed/org/apache/cassandra/distributed/test/cql3/SingleNodeTableWalkTest.java
@@ -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()
{
diff --git
a/test/distributed/org/apache/cassandra/distributed/test/cql3/SingleNodeTokenConflictTest.java
b/test/distributed/org/apache/cassandra/distributed/test/cql3/SingleNodeTokenConflictTest.java
index 7c8665f216..e866ed9a34 100644
---
a/test/distributed/org/apache/cassandra/distributed/test/cql3/SingleNodeTokenConflictTest.java
+++
b/test/distributed/org/apache/cassandra/distributed/test/cql3/SingleNodeTokenConflictTest.java
@@ -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),
diff --git
a/test/distributed/org/apache/cassandra/distributed/test/cql3/StatefulASTBase.java
b/test/distributed/org/apache/cassandra/distributed/test/cql3/StatefulASTBase.java
index 0771b8f847..cec98dcbea 100644
---
a/test/distributed/org/apache/cassandra/distributed/test/cql3/StatefulASTBase.java
+++
b/test/distributed/org/apache/cassandra/distributed/test/cql3/StatefulASTBase.java
@@ -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();
diff --git
a/test/harry/main/org/apache/cassandra/harry/model/ASTSingleTableModel.java
b/test/harry/main/org/apache/cassandra/harry/model/ASTSingleTableModel.java
index 52c30be260..eb130e6c77 100644
--- a/test/harry/main/org/apache/cassandra/harry/model/ASTSingleTableModel.java
+++ b/test/harry/main/org/apache/cassandra/harry/model/ASTSingleTableModel.java
@@ -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<>();
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java
b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index 9f0369b79c..1dad1a5ae7 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -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
{
diff --git a/test/unit/org/apache/cassandra/cql3/KnownIssue.java
b/test/unit/org/apache/cassandra/cql3/KnownIssue.java
index 19378ce496..35caf0f01b 100644
--- a/test/unit/org/apache/cassandra/cql3/KnownIssue.java
+++ b/test/unit/org/apache/cassandra/cql3/KnownIssue.java
@@ -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",
diff --git a/test/unit/org/apache/cassandra/cql3/ast/Select.java
b/test/unit/org/apache/cassandra/cql3/ast/Select.java
index 8a451c849a..7e8eccf06c 100644
--- a/test/unit/org/apache/cassandra/cql3/ast/Select.java
+++ b/test/unit/org/apache/cassandra/cql3/ast/Select.java
@@ -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;
}
diff --git
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
index 783f4314f6..f6b2a693ce 100644
---
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
+++
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
@@ -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)");
diff --git
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java
index 5024eeaf41..1ebeec495e 100644
---
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java
+++
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java
@@ -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"));
});
}
}
diff --git a/test/unit/org/apache/cassandra/utils/ImmutableUniqueList.java
b/test/unit/org/apache/cassandra/utils/ImmutableUniqueList.java
index 4d156b58be..dfa57dc5d5 100644
--- a/test/unit/org/apache/cassandra/utils/ImmutableUniqueList.java
+++ b/test/unit/org/apache/cassandra/utils/ImmutableUniqueList.java
@@ -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)
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]