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 2014/04/02 20:17:40 UTC
[1/2] git commit: Fix bad skip of sstables on slice query with
composite start/finish
Repository: cassandra
Updated Branches:
refs/heads/cassandra-2.1 486cc4cc0 -> ab1a02cfa
Fix bad skip of sstables on slice query with composite start/finish
Patch by Tyler Hobbs; reviewed by Sylvain Lebresne for CASSANDRA-6825
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b218536d
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b218536d
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b218536d
Branch: refs/heads/cassandra-2.1
Commit: b218536dbc55232a73a81f6288d82ef2b0fa0e6f
Parents: e1a9093
Author: Tyler Hobbs <ty...@datastax.com>
Authored: Wed Apr 2 12:02:47 2014 -0500
Committer: Tyler Hobbs <ty...@datastax.com>
Committed: Wed Apr 2 12:02:47 2014 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cql3/statements/SelectStatement.java | 7 +-
.../cassandra/db/marshal/CompositeType.java | 40 ++-
.../service/pager/AbstractQueryPager.java | 11 +
.../service/pager/SliceQueryPager.java | 5 +
.../cassandra/db/marshal/CompositeTypeTest.java | 294 +++++++++++++++++++
6 files changed, 350 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b218536d/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 483ee0b..3cc9937 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -40,6 +40,7 @@
* Fix LIMT with static columns (CASSANDRA-6956)
* Fix clash with CQL column name in thrift validation (CASSANDRA-6892)
* Fix error with super columns in mixed 1.2-2.0 clusters (CASSANDRA-6966)
+ * Fix bad skip of sstables on slice query with composite start/finish (CASSANDRA-6825)
Merged from 1.2:
* Add UNLOGGED, COUNTER options to BATCH documentation (CASSANDRA-6816)
* add extra SSL cipher suites (CASSANDRA-6613)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b218536d/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 53b2c05..56e87e8 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -22,9 +22,7 @@ import java.util.*;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
-import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import org.github.jamm.MemoryMeter;
@@ -53,6 +51,8 @@ import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Encapsulates a completely parsed SELECT query, including the target
@@ -61,6 +61,8 @@ import org.apache.cassandra.utils.Pair;
*/
public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
{
+ private static final Logger logger = LoggerFactory.getLogger(SelectStatement.class);
+
private static final int DEFAULT_COUNT_PAGE_SIZE = 10000;
private final int boundTerms;
@@ -257,6 +259,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
while (!pager.isExhausted())
{
int maxLimit = pager.maxRemaining();
+ logger.debug("New maxLimit for paged count query is {}", maxLimit);
ResultSet rset = process(pager.fetchPage(pageSize), variables, maxLimit, now);
count += rset.rows.size();
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b218536d/src/java/org/apache/cassandra/db/marshal/CompositeType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/CompositeType.java b/src/java/org/apache/cassandra/db/marshal/CompositeType.java
index 83e3b97..7f08219 100644
--- a/src/java/org/apache/cassandra/db/marshal/CompositeType.java
+++ b/src/java/org/apache/cassandra/db/marshal/CompositeType.java
@@ -264,26 +264,54 @@ public class CompositeType extends AbstractCompositeType
public boolean intersects(List<ByteBuffer> minColumnNames, List<ByteBuffer> maxColumnNames, SliceQueryFilter filter)
{
assert minColumnNames.size() == maxColumnNames.size();
+
+ // If any of the slices in the filter intersect, return true
outer:
for (ColumnSlice slice : filter.slices)
{
- // This slices intersects if all component intersect. And we don't intersect
- // only if no slice intersects
ByteBuffer[] start = split(filter.isReversed() ? slice.finish : slice.start);
ByteBuffer[] finish = split(filter.isReversed() ? slice.start : slice.finish);
- for (int i = 0; i < minColumnNames.size(); i++)
+
+ if (compare(start, maxColumnNames, true) > 0 || compare(finish, minColumnNames, false) < 0)
+ continue; // slice does not intersect
+
+ // We could safely return true here, but there's a minor optimization: if the first component is restricted
+ // to a single value, we can check that the second component falls within the min/max for that component
+ // (and repeat for all components).
+ for (int i = 0; i < Math.min(Math.min(start.length, finish.length), minColumnNames.size()); i++)
{
AbstractType<?> t = types.get(i);
- ByteBuffer s = i < start.length ? start[i] : ByteBufferUtil.EMPTY_BYTE_BUFFER;
- ByteBuffer f = i < finish.length ? finish[i] : ByteBufferUtil.EMPTY_BYTE_BUFFER;
- if (!t.intersects(minColumnNames.get(i), maxColumnNames.get(i), s, f))
+
+ // we already know the first component falls within its min/max range (otherwise we wouldn't get here)
+ if (i > 0 && !t.intersects(minColumnNames.get(i), maxColumnNames.get(i), start[i], finish[i]))
continue outer;
+
+ // if this component isn't equal in the start and finish, we don't need to check any more
+ if (t.compare(start[i], finish[i]) != 0)
+ break;
}
return true;
}
+
+ // none of the slices intersected
return false;
}
+ /** Helper method for intersects() */
+ private int compare(ByteBuffer[] sliceBounds, List<ByteBuffer> sstableBounds, boolean isSliceStart)
+ {
+ for (int i = 0; i < sstableBounds.size(); i++)
+ {
+ if (i >= sliceBounds.length)
+ return isSliceStart ? -1 : 1;
+
+ int comparison = types.get(i).compare(sliceBounds[i], sstableBounds.get(i));
+ if (comparison != 0)
+ return comparison;
+ }
+ return 0;
+ }
+
private static class StaticParsedComparator implements ParsedComparator
{
final AbstractType<?> type;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b218536d/src/java/org/apache/cassandra/service/pager/AbstractQueryPager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/pager/AbstractQueryPager.java b/src/java/org/apache/cassandra/service/pager/AbstractQueryPager.java
index 1b4bdbd..4210296 100644
--- a/src/java/org/apache/cassandra/service/pager/AbstractQueryPager.java
+++ b/src/java/org/apache/cassandra/service/pager/AbstractQueryPager.java
@@ -32,9 +32,13 @@ import org.apache.cassandra.db.filter.ColumnCounter;
import org.apache.cassandra.db.filter.IDiskAtomFilter;
import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.exceptions.RequestValidationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
abstract class AbstractQueryPager implements QueryPager
{
+ private static final Logger logger = LoggerFactory.getLogger(AbstractQueryPager.class);
+
private final ConsistencyLevel consistencyLevel;
private final boolean localQuery;
@@ -85,11 +89,13 @@ abstract class AbstractQueryPager implements QueryPager
if (rows.isEmpty())
{
+ logger.debug("Got empty set of rows, considering pager exhausted");
exhausted = true;
return Collections.emptyList();
}
int liveCount = getPageLiveCount(rows);
+ logger.debug("Fetched {} live rows", liveCount);
// Because SP.getRangeSlice doesn't trim the result (see SP.trim()), liveCount may be greater than what asked
// (currentPageSize). This would throw off the paging logic so we trim the excess. It's not extremely efficient
@@ -105,7 +111,10 @@ abstract class AbstractQueryPager implements QueryPager
// If we've got less than requested, there is no more query to do (but
// we still need to return the current page)
if (liveCount < currentPageSize)
+ {
+ logger.debug("Got result ({}) smaller than page size ({}), considering pager exhausted", liveCount, currentPageSize);
exhausted = true;
+ }
// If it's not the first query and the first column is the last one returned (likely
// but not certain since paging can race with deletes/expiration), then remove the
@@ -124,6 +133,8 @@ abstract class AbstractQueryPager implements QueryPager
remaining++;
}
+ logger.debug("Remaining rows to page: {}", remaining);
+
if (!isExhausted())
lastWasRecorded = recordLast(rows.get(rows.size() - 1));
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b218536d/src/java/org/apache/cassandra/service/pager/SliceQueryPager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/pager/SliceQueryPager.java b/src/java/org/apache/cassandra/service/pager/SliceQueryPager.java
index c94f7f6..ec229cb 100644
--- a/src/java/org/apache/cassandra/service/pager/SliceQueryPager.java
+++ b/src/java/org/apache/cassandra/service/pager/SliceQueryPager.java
@@ -26,12 +26,16 @@ import org.apache.cassandra.db.filter.SliceQueryFilter;
import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.service.StorageProxy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Pager over a SliceFromReadCommand.
*/
public class SliceQueryPager extends AbstractQueryPager implements SinglePartitionPager
{
+ private static final Logger logger = LoggerFactory.getLogger(SliceQueryPager.class);
+
private final SliceFromReadCommand command;
private volatile ByteBuffer lastReturned;
@@ -73,6 +77,7 @@ public class SliceQueryPager extends AbstractQueryPager implements SinglePartiti
if (lastReturned != null)
filter = filter.withUpdatedStart(lastReturned, cfm.comparator);
+ logger.debug("Querying next page of slice query; new filter: {}", filter);
ReadCommand pageCmd = command.withUpdatedFilter(filter);
return localQuery
? Collections.singletonList(pageCmd.getRow(Keyspace.open(command.ksName)))
http://git-wip-us.apache.org/repos/asf/cassandra/blob/b218536d/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java b/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
index 1039fb6..20cb5ef 100644
--- a/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
+++ b/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
@@ -24,10 +24,14 @@ import java.util.Iterator;
import java.util.List;
import java.util.UUID;
+import org.apache.cassandra.db.filter.ColumnSlice;
+import org.apache.cassandra.db.filter.SliceQueryFilter;
import org.apache.cassandra.serializers.MarshalException;
import org.junit.Test;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.Util;
@@ -255,6 +259,296 @@ public class CompositeTypeTest extends SchemaLoader
}
}
+ @Test
+ public void testIntersectsSingleSlice()
+ {
+ CompositeType comparator = CompositeType.getInstance(Int32Type.instance, Int32Type.instance, Int32Type.instance);
+
+ // filter falls entirely before sstable
+ SliceQueryFilter filter = new SliceQueryFilter(composite(0, 0, 0), composite(1, 0, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), filter));
+
+ // same case, but with empty start
+ filter = new SliceQueryFilter(ByteBufferUtil.EMPTY_BYTE_BUFFER, composite(1, 0, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), filter));
+
+ // same case, but with missing components for start
+ filter = new SliceQueryFilter(composite(0), composite(1, 0, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), filter));
+
+ // same case, but with missing components for start and end
+ filter = new SliceQueryFilter(composite(0), composite(1, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), filter));
+
+
+ // end of slice matches start of sstable for the first component, but not the second component
+ filter = new SliceQueryFilter(composite(0, 0, 0), composite(1, 0, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), filter));
+
+ // same case, but with missing components for start
+ filter = new SliceQueryFilter(composite(0), composite(1, 0, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), filter));
+
+ // same case, but with missing components for start and end
+ filter = new SliceQueryFilter(composite(0), composite(1, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), filter));
+
+ // first two components match, but not the last
+ filter = new SliceQueryFilter(composite(0, 0, 0), composite(1, 1, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 1, 1), columnNames(3, 1, 1), filter));
+
+ // all three components in slice end match the start of the sstable
+ filter = new SliceQueryFilter(composite(0, 0, 0), composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 1, 1), columnNames(3, 1, 1), filter));
+
+
+ // filter falls entirely after sstable
+ filter = new SliceQueryFilter(composite(4, 0, 0), composite(4, 0, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), filter));
+
+ // same case, but with empty end
+ filter = new SliceQueryFilter(composite(4, 0, 0), ByteBufferUtil.EMPTY_BYTE_BUFFER, false, 1);
+ assertFalse(comparator.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), filter));
+
+ // same case, but with missing components for end
+ filter = new SliceQueryFilter(composite(4, 0, 0), composite(1), false, 1);
+ assertFalse(comparator.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), filter));
+
+ // same case, but with missing components for start and end
+ filter = new SliceQueryFilter(composite(4, 0), composite(1), false, 1);
+ assertFalse(comparator.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), filter));
+
+
+ // start of slice matches end of sstable for the first component, but not the second component
+ filter = new SliceQueryFilter(composite(1, 1, 1), composite(2, 0, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(0, 0, 0), columnNames(1, 0, 0), filter));
+
+ // start of slice matches end of sstable for the first two components, but not the last component
+ filter = new SliceQueryFilter(composite(1, 1, 1), composite(2, 0, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(0, 0, 0), columnNames(1, 1, 0), filter));
+
+ // all three components in the slice start match the end of the sstable
+ filter = new SliceQueryFilter(composite(1, 1, 1), composite(2, 0, 0), false, 1);
+ assertTrue(comparator.intersects(columnNames(0, 0, 0), columnNames(1, 1, 1), filter));
+
+
+ // slice covers entire sstable (with no matching edges)
+ filter = new SliceQueryFilter(composite(0, 0, 0), composite(2, 0, 0), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+ // same case, but with empty ends
+ filter = new SliceQueryFilter(ByteBufferUtil.EMPTY_BYTE_BUFFER, ByteBufferUtil.EMPTY_BYTE_BUFFER, false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+ // same case, but with missing components
+ filter = new SliceQueryFilter(composite(0), composite(2, 0), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+ // slice covers entire sstable (with matching start)
+ filter = new SliceQueryFilter(composite(1, 0, 0), composite(2, 0, 0), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+ // slice covers entire sstable (with matching end)
+ filter = new SliceQueryFilter(composite(0, 0, 0), composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+ // slice covers entire sstable (with matching start and end)
+ filter = new SliceQueryFilter(composite(1, 0, 0), composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+
+ // slice falls entirely within sstable (with matching start)
+ filter = new SliceQueryFilter(composite(1, 0, 0), composite(1, 1, 0), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+ // same case, but with a missing end component
+ filter = new SliceQueryFilter(composite(1, 0, 0), composite(1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+ // slice falls entirely within sstable (with matching end)
+ filter = new SliceQueryFilter(composite(1, 1, 0), composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+ // same case, but with a missing start component
+ filter = new SliceQueryFilter(composite(1, 1), composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), filter));
+
+
+ // slice falls entirely within sstable
+ filter = new SliceQueryFilter(composite(1, 1, 0), composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), filter));
+
+ // same case, but with a missing start component
+ filter = new SliceQueryFilter(composite(1, 1), composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), filter));
+
+ // same case, but with a missing start and end components
+ filter = new SliceQueryFilter(composite(1), composite(1, 2), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), filter));
+
+ // slice falls entirely within sstable (slice start and end are the same)
+ filter = new SliceQueryFilter(composite(1, 1, 1), composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), filter));
+
+
+ // slice starts within sstable, empty end
+ filter = new SliceQueryFilter(composite(1, 1, 1), ByteBufferUtil.EMPTY_BYTE_BUFFER, false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // same case, but with missing end components
+ filter = new SliceQueryFilter(composite(1, 1, 1), composite(3), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // slice starts within sstable (matching sstable start), empty end
+ filter = new SliceQueryFilter(composite(1, 0, 0), ByteBufferUtil.EMPTY_BYTE_BUFFER, false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // same case, but with missing end components
+ filter = new SliceQueryFilter(composite(1, 0, 0), composite(3), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // slice starts within sstable (matching sstable end), empty end
+ filter = new SliceQueryFilter(composite(2, 0, 0), ByteBufferUtil.EMPTY_BYTE_BUFFER, false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // same case, but with missing end components
+ filter = new SliceQueryFilter(composite(2, 0, 0), composite(3), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+
+ // slice ends within sstable, empty end
+ filter = new SliceQueryFilter(ByteBufferUtil.EMPTY_BYTE_BUFFER, composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // same case, but with missing start components
+ filter = new SliceQueryFilter(composite(0), composite(1, 1, 1), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // slice ends within sstable (matching sstable start), empty start
+ filter = new SliceQueryFilter(ByteBufferUtil.EMPTY_BYTE_BUFFER, composite(1, 0, 0), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // same case, but with missing start components
+ filter = new SliceQueryFilter(composite(0), composite(1, 0, 0), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // slice ends within sstable (matching sstable end), empty start
+ filter = new SliceQueryFilter(ByteBufferUtil.EMPTY_BYTE_BUFFER, composite(2, 0, 0), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+ // same case, but with missing start components
+ filter = new SliceQueryFilter(composite(0), composite(2, 0, 0), false, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), filter));
+
+
+ // the slice technically falls within the sstable range, but since the first component is restricted to
+ // a single value, we can check that the second component does not fall within its min/max
+ filter = new SliceQueryFilter(composite(1, 2, 0), composite(1, 3, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), filter));
+
+ // same case, but with a missing start component
+ filter = new SliceQueryFilter(composite(1, 2), composite(1, 3, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), filter));
+
+ // same case, but with a missing end component
+ filter = new SliceQueryFilter(composite(1, 2, 0), composite(1, 3), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), filter));
+
+ // same case, but with a missing start and end components
+ filter = new SliceQueryFilter(composite(1, 2), composite(1, 3), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), filter));
+
+
+ // same as the previous set of tests, but the second component is equal in the slice start and end
+ filter = new SliceQueryFilter(composite(1, 2, 0), composite(1, 2, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), filter));
+
+ // same case, but with a missing start component
+ filter = new SliceQueryFilter(composite(1, 2), composite(1, 2, 0), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), filter));
+
+ // same case, but with a missing end component
+ filter = new SliceQueryFilter(composite(1, 2, 0), composite(1, 2), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), filter));
+
+ // same case, but with a missing start and end components
+ filter = new SliceQueryFilter(composite(1, 2), composite(1, 2), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), filter));
+
+ // same as the previous tests, but it's the third component that doesn't fit in its range this time
+ filter = new SliceQueryFilter(composite(1, 1, 2), composite(1, 1, 3), false, 1);
+ assertFalse(comparator.intersects(columnNames(1, 1, 0), columnNames(2, 2, 1), filter));
+
+
+ // basic check on reversed slices
+ filter = new SliceQueryFilter(composite(1, 0, 0), composite(0, 0, 0), true, 1);
+ assertFalse(comparator.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), filter));
+
+ filter = new SliceQueryFilter(composite(1, 0, 0), composite(0, 0, 0), true, 1);
+ assertFalse(comparator.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), filter));
+
+ filter = new SliceQueryFilter(composite(1, 1, 1), composite(1, 1, 0), true, 1);
+ assertTrue(comparator.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), filter));
+ }
+
+ @Test
+ public void testIntersectsMultipleSlices()
+ {
+ CompositeType comparator = CompositeType.getInstance(Int32Type.instance, Int32Type.instance, Int32Type.instance);
+
+ // all slices intersect
+ SliceQueryFilter filter = new SliceQueryFilter(new ColumnSlice[]{
+ new ColumnSlice(composite(1, 0, 0), composite(2, 0, 0)),
+ new ColumnSlice(composite(3, 0, 0), composite(4, 0, 0)),
+ new ColumnSlice(composite(5, 0, 0), composite(6, 0, 0)),
+ }, false, 1);
+
+ // first slice doesn't intersect
+ assertTrue(comparator.intersects(columnNames(0, 0, 0), columnNames(7, 0, 0), filter));
+ filter = new SliceQueryFilter(new ColumnSlice[]{
+ new ColumnSlice(composite(1, 0, 0), composite(2, 0, 0)),
+ new ColumnSlice(composite(3, 0, 0), composite(4, 0, 0)),
+ new ColumnSlice(composite(5, 0, 0), composite(6, 0, 0)),
+ }, false, 1);
+ assertTrue(comparator.intersects(columnNames(3, 0, 0), columnNames(7, 0, 0), filter));
+
+ // first two slices don't intersect
+ assertTrue(comparator.intersects(columnNames(0, 0, 0), columnNames(7, 0, 0), filter));
+ filter = new SliceQueryFilter(new ColumnSlice[]{
+ new ColumnSlice(composite(1, 0, 0), composite(2, 0, 0)),
+ new ColumnSlice(composite(3, 0, 0), composite(4, 0, 0)),
+ new ColumnSlice(composite(5, 0, 0), composite(6, 0, 0)),
+ }, false, 1);
+ assertTrue(comparator.intersects(columnNames(5, 0, 0), columnNames(7, 0, 0), filter));
+
+ // none of the slices intersect
+ assertTrue(comparator.intersects(columnNames(0, 0, 0), columnNames(7, 0, 0), filter));
+ filter = new SliceQueryFilter(new ColumnSlice[]{
+ new ColumnSlice(composite(1, 0, 0), composite(2, 0, 0)),
+ new ColumnSlice(composite(3, 0, 0), composite(4, 0, 0)),
+ new ColumnSlice(composite(5, 0, 0), composite(6, 0, 0)),
+ }, false, 1);
+ assertFalse(comparator.intersects(columnNames(7, 0, 0), columnNames(8, 0, 0), filter));
+ }
+
+
+ private static ByteBuffer composite(Integer ... components)
+ {
+ CompositeType comparator = CompositeType.getInstance(Int32Type.instance, Int32Type.instance, Int32Type.instance);
+ CompositeType.Builder builder = comparator.builder();
+ for (int component : components)
+ builder.add(ByteBufferUtil.bytes(component));
+ return builder.build();
+ }
+
+ private static List<ByteBuffer> columnNames(Integer ... components)
+ {
+ List<ByteBuffer> names = new ArrayList<>(components.length);
+ for (int component : components)
+ names.add(ByteBufferUtil.bytes(component));
+ return names;
+ }
+
private void addColumn(RowMutation rm, ByteBuffer cname)
{
rm.add(cfName, cname, ByteBufferUtil.EMPTY_BYTE_BUFFER, 0);
[2/2] git commit: Merge branch 'cassandra-2.0' into cassandra-2.1
Posted by ty...@apache.org.
Merge branch 'cassandra-2.0' into cassandra-2.1
Conflicts:
src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
src/java/org/apache/cassandra/db/marshal/CompositeType.java
test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/ab1a02cf
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/ab1a02cf
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/ab1a02cf
Branch: refs/heads/cassandra-2.1
Commit: ab1a02cfa69e0c57579b312202b477e43bb90b45
Parents: 486cc4c b218536
Author: Tyler Hobbs <ty...@datastax.com>
Authored: Wed Apr 2 13:17:18 2014 -0500
Committer: Tyler Hobbs <ty...@datastax.com>
Committed: Wed Apr 2 13:17:18 2014 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cql3/statements/SelectStatement.java | 7 +-
.../apache/cassandra/db/filter/ColumnSlice.java | 32 +-
.../service/pager/AbstractQueryPager.java | 11 +
.../service/pager/SliceQueryPager.java | 5 +
.../cassandra/db/filter/ColumnSliceTest.java | 290 +++++++++++++++++++
6 files changed, 342 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ab1a02cf/CHANGES.txt
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ab1a02cf/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index edc7f5c,56e87e8..7ca7d93
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@@ -22,9 -22,8 +22,10 @@@ import java.util.*
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
+import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+
import org.github.jamm.MemoryMeter;
import org.apache.cassandra.auth.Permission;
@@@ -38,7 -37,7 +39,6 @@@ import org.apache.cassandra.db.filter.*
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.dht.*;
import org.apache.cassandra.exceptions.*;
--import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.service.StorageProxy;
@@@ -49,6 -50,9 +49,8 @@@ import org.apache.cassandra.thrift.Thri
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
-import org.apache.cassandra.utils.Pair;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
/**
* Encapsulates a completely parsed SELECT query, including the target
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ab1a02cf/src/java/org/apache/cassandra/db/filter/ColumnSlice.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/filter/ColumnSlice.java
index 262ebae,9eff12a..3838ee5
--- a/src/java/org/apache/cassandra/db/filter/ColumnSlice.java
+++ b/src/java/org/apache/cassandra/db/filter/ColumnSlice.java
@@@ -52,39 -47,22 +52,65 @@@ public class ColumnSlic
this.finish = finish;
}
- public boolean isAlwaysEmpty(AbstractType<?> comparator, boolean reversed)
+ public boolean isAlwaysEmpty(CellNameType comparator, boolean reversed)
{
- Comparator<ByteBuffer> orderedComparator = reversed ? comparator.reverseComparator : comparator;
- return (start.remaining() > 0 && finish.remaining() > 0 && orderedComparator.compare(start, finish) > 0);
+ Comparator<Composite> orderedComparator = reversed ? comparator.reverseComparator() : comparator;
+ return !start.isEmpty() && !finish.isEmpty() && orderedComparator.compare(start, finish) > 0;
}
- public boolean includes(Comparator<ByteBuffer> cmp, ByteBuffer name)
+ public boolean includes(Comparator<Composite> cmp, Composite name)
{
- return cmp.compare(start, name) <= 0 && (finish.equals(ByteBufferUtil.EMPTY_BYTE_BUFFER) || cmp.compare(finish, name) >= 0);
+ return cmp.compare(start, name) <= 0 && (finish.isEmpty() || cmp.compare(finish, name) >= 0);
+ }
+
+ public boolean isBefore(Comparator<Composite> cmp, Composite name)
+ {
+ return !finish.isEmpty() && cmp.compare(finish, name) < 0;
+ }
+
+ public boolean intersects(List<ByteBuffer> minCellNames, List<ByteBuffer> maxCellNames, CellNameType comparator, boolean reversed)
+ {
+ assert minCellNames.size() == maxCellNames.size();
+
+ Composite sStart = reversed ? finish : start;
+ Composite sEnd = reversed ? start : finish;
+
- for (int i = 0; i < minCellNames.size(); i++)
++ if (compare(sStart, maxCellNames, comparator, true) > 0 || compare(sEnd, minCellNames, comparator, false) < 0)
++ return false;
++
++ // We could safely return true here, but there's a minor optimization: if the first component is restricted
++ // to a single value, we can check that the second component falls within the min/max for that component
++ // (and repeat for all components).
++ for (int i = 0; i < Math.min(Math.min(sStart.size(), sEnd.size()), minCellNames.size()); i++)
+ {
+ AbstractType<?> t = comparator.subtype(i);
- if ( (i < sEnd.size() && t.compare(sEnd.get(i), minCellNames.get(i)) < 0)
- || (i < sStart.size() && t.compare(sStart.get(i), maxCellNames.get(i)) > 0))
++ // we already know the first component falls within its min/max range (otherwise we wouldn't get here)
++ if (i > 0 && (t.compare(sEnd.get(i), minCellNames.get(i)) < 0 || t.compare(sStart.get(i), maxCellNames.get(i)) > 0))
+ return false;
++
++ // if this component isn't equal in the start and finish, we don't need to check any more
++ if (t.compare(sStart.get(i), sEnd.get(i)) != 0)
++ break;
+ }
++
+ return true;
}
- public boolean isBefore(Comparator<ByteBuffer> cmp, ByteBuffer name)
++ /** Helper method for intersects() */
++ private int compare(Composite sliceBounds, List<ByteBuffer> sstableBounds, CellNameType comparator, boolean isSliceStart)
+ {
- return !finish.equals(ByteBufferUtil.EMPTY_BYTE_BUFFER) && cmp.compare(finish, name) < 0;
++ for (int i = 0; i < sstableBounds.size(); i++)
++ {
++ if (i >= sliceBounds.size())
++ return isSliceStart ? -1 : 1;
++
++ int comparison = comparator.subtype(i).compare(sliceBounds.get(i), sstableBounds.get(i));
++ if (comparison != 0)
++ return comparison;
++ }
++ return 0;
+ }
+
@Override
public final int hashCode()
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ab1a02cf/src/java/org/apache/cassandra/service/pager/AbstractQueryPager.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ab1a02cf/src/java/org/apache/cassandra/service/pager/SliceQueryPager.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/service/pager/SliceQueryPager.java
index 3a8c81f,ec229cb..8108098
--- a/src/java/org/apache/cassandra/service/pager/SliceQueryPager.java
+++ b/src/java/org/apache/cassandra/service/pager/SliceQueryPager.java
@@@ -33,9 -34,11 +35,11 @@@ import org.slf4j.LoggerFactory
*/
public class SliceQueryPager extends AbstractQueryPager implements SinglePartitionPager
{
+ private static final Logger logger = LoggerFactory.getLogger(SliceQueryPager.class);
+
private final SliceFromReadCommand command;
- private volatile ByteBuffer lastReturned;
+ private volatile CellName lastReturned;
// Don't use directly, use QueryPagers method instead
SliceQueryPager(SliceFromReadCommand command, ConsistencyLevel consistencyLevel, boolean localQuery)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ab1a02cf/test/unit/org/apache/cassandra/db/filter/ColumnSliceTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/db/filter/ColumnSliceTest.java
index 0000000,0000000..4718795
new file mode 100644
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/filter/ColumnSliceTest.java
@@@ -1,0 -1,0 +1,290 @@@
++/*
++ * * 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.db.filter;
++
++import org.apache.cassandra.db.composites.Composite;
++import org.apache.cassandra.db.composites.CompoundDenseCellNameType;
++import org.apache.cassandra.db.marshal.AbstractType;
++import org.apache.cassandra.db.marshal.Int32Type;
++import org.apache.cassandra.utils.ByteBufferUtil;
++import org.junit.Test;
++
++import java.nio.ByteBuffer;
++import java.util.ArrayList;
++import java.util.List;
++
++import static org.junit.Assert.assertFalse;
++import static org.junit.Assert.assertTrue;
++
++public class ColumnSliceTest
++{
++ @Test
++ public void testIntersectsSingleSlice()
++ {
++ List<AbstractType<?>> types = new ArrayList<>();
++ types.add(Int32Type.instance);
++ types.add(Int32Type.instance);
++ types.add(Int32Type.instance);
++ CompoundDenseCellNameType nameType = new CompoundDenseCellNameType(types);
++
++ // filter falls entirely before sstable
++ ColumnSlice slice = new ColumnSlice(composite(0, 0, 0), composite(1, 0, 0));
++ assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
++
++ // same case, but with empty start
++ slice = new ColumnSlice(composite(), composite(1, 0, 0));
++ assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
++
++ // same case, but with missing components for start
++ slice = new ColumnSlice(composite(0), composite(1, 0, 0));
++ assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
++
++ // same case, but with missing components for start and end
++ slice = new ColumnSlice(composite(0), composite(1, 0));
++ assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
++
++
++ // end of slice matches start of sstable for the first component, but not the second component
++ slice = new ColumnSlice(composite(0, 0, 0), composite(1, 0, 0));
++ assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), nameType, false));
++
++ // same case, but with missing components for start
++ slice = new ColumnSlice(composite(0), composite(1, 0, 0));
++ assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), nameType, false));
++
++ // same case, but with missing components for start and end
++ slice = new ColumnSlice(composite(0), composite(1, 0));
++ assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), nameType, false));
++
++ // first two components match, but not the last
++ slice = new ColumnSlice(composite(0, 0, 0), composite(1, 1, 0));
++ assertFalse(slice.intersects(columnNames(1, 1, 1), columnNames(3, 1, 1), nameType, false));
++
++ // all three components in slice end match the start of the sstable
++ slice = new ColumnSlice(composite(0, 0, 0), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 1, 1), columnNames(3, 1, 1), nameType, false));
++
++
++ // filter falls entirely after sstable
++ slice = new ColumnSlice(composite(4, 0, 0), composite(4, 0, 0));
++ assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
++
++ // same case, but with empty end
++ slice = new ColumnSlice(composite(4, 0, 0), composite());
++ assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
++
++ // same case, but with missing components for end
++ slice = new ColumnSlice(composite(4, 0, 0), composite(1));
++ assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
++
++ // same case, but with missing components for start and end
++ slice = new ColumnSlice(composite(4, 0), composite(1));
++ assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
++
++
++ // start of slice matches end of sstable for the first component, but not the second component
++ slice = new ColumnSlice(composite(1, 1, 1), composite(2, 0, 0));
++ assertFalse(slice.intersects(columnNames(0, 0, 0), columnNames(1, 0, 0), nameType, false));
++
++ // start of slice matches end of sstable for the first two components, but not the last component
++ slice = new ColumnSlice(composite(1, 1, 1), composite(2, 0, 0));
++ assertFalse(slice.intersects(columnNames(0, 0, 0), columnNames(1, 1, 0), nameType, false));
++
++ // all three components in the slice start match the end of the sstable
++ slice = new ColumnSlice(composite(1, 1, 1), composite(2, 0, 0));
++ assertTrue(slice.intersects(columnNames(0, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++
++ // slice covers entire sstable (with no matching edges)
++ slice = new ColumnSlice(composite(0, 0, 0), composite(2, 0, 0));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++ // same case, but with empty ends
++ slice = new ColumnSlice(composite(), composite());
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++ // same case, but with missing components
++ slice = new ColumnSlice(composite(0), composite(2, 0));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++ // slice covers entire sstable (with matching start)
++ slice = new ColumnSlice(composite(1, 0, 0), composite(2, 0, 0));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++ // slice covers entire sstable (with matching end)
++ slice = new ColumnSlice(composite(0, 0, 0), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++ // slice covers entire sstable (with matching start and end)
++ slice = new ColumnSlice(composite(1, 0, 0), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++
++ // slice falls entirely within sstable (with matching start)
++ slice = new ColumnSlice(composite(1, 0, 0), composite(1, 1, 0));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++ // same case, but with a missing end component
++ slice = new ColumnSlice(composite(1, 0, 0), composite(1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++ // slice falls entirely within sstable (with matching end)
++ slice = new ColumnSlice(composite(1, 1, 0), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++ // same case, but with a missing start component
++ slice = new ColumnSlice(composite(1, 1), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
++
++
++ // slice falls entirely within sstable
++ slice = new ColumnSlice(composite(1, 1, 0), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, false));
++
++ // same case, but with a missing start component
++ slice = new ColumnSlice(composite(1, 1), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, false));
++
++ // same case, but with a missing start and end components
++ slice = new ColumnSlice(composite(1), composite(1, 2));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, false));
++
++ // slice falls entirely within sstable (slice start and end are the same)
++ slice = new ColumnSlice(composite(1, 1, 1), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, false));
++
++
++ // slice starts within sstable, empty end
++ slice = new ColumnSlice(composite(1, 1, 1), composite());
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // same case, but with missing end components
++ slice = new ColumnSlice(composite(1, 1, 1), composite(3));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // slice starts within sstable (matching sstable start), empty end
++ slice = new ColumnSlice(composite(1, 0, 0), composite());
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // same case, but with missing end components
++ slice = new ColumnSlice(composite(1, 0, 0), composite(3));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // slice starts within sstable (matching sstable end), empty end
++ slice = new ColumnSlice(composite(2, 0, 0), composite());
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // same case, but with missing end components
++ slice = new ColumnSlice(composite(2, 0, 0), composite(3));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++
++ // slice ends within sstable, empty end
++ slice = new ColumnSlice(composite(), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // same case, but with missing start components
++ slice = new ColumnSlice(composite(0), composite(1, 1, 1));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // slice ends within sstable (matching sstable start), empty start
++ slice = new ColumnSlice(composite(), composite(1, 0, 0));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // same case, but with missing start components
++ slice = new ColumnSlice(composite(0), composite(1, 0, 0));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // slice ends within sstable (matching sstable end), empty start
++ slice = new ColumnSlice(composite(), composite(2, 0, 0));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++ // same case, but with missing start components
++ slice = new ColumnSlice(composite(0), composite(2, 0, 0));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
++
++
++ // the slice technically falls within the sstable range, but since the first component is restricted to
++ // a single value, we can check that the second component does not fall within its min/max
++ slice = new ColumnSlice(composite(1, 2, 0), composite(1, 3, 0));
++ assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
++
++ // same case, but with a missing start component
++ slice = new ColumnSlice(composite(1, 2), composite(1, 3, 0));
++ assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
++
++ // same case, but with a missing end component
++ slice = new ColumnSlice(composite(1, 2, 0), composite(1, 3));
++ assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
++
++ // same case, but with a missing start and end components
++ slice = new ColumnSlice(composite(1, 2), composite(1, 3));
++ assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
++
++
++ // same as the previous set of tests, but the second component is equal in the slice start and end
++ slice = new ColumnSlice(composite(1, 2, 0), composite(1, 2, 0));
++ assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
++
++ // same case, but with a missing start component
++ slice = new ColumnSlice(composite(1, 2), composite(1, 2, 0));
++ assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
++
++ // same case, but with a missing end component
++ slice = new ColumnSlice(composite(1, 2, 0), composite(1, 2));
++ assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
++
++ // same case, but with a missing start and end components
++ slice = new ColumnSlice(composite(1, 2), composite(1, 2));
++ assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
++
++ // same as the previous tests, but it's the third component that doesn't fit in its range this time
++ slice = new ColumnSlice(composite(1, 1, 2), composite(1, 1, 3));
++ assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(2, 2, 1), nameType, false));
++
++
++ // basic check on reversed slices
++ slice = new ColumnSlice(composite(1, 0, 0), composite(0, 0, 0));
++ assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, true));
++
++ slice = new ColumnSlice(composite(1, 0, 0), composite(0, 0, 0));
++ assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), nameType, true));
++
++ slice = new ColumnSlice(composite(1, 1, 1), composite(1, 1, 0));
++ assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, true));
++ }
++
++ private static Composite composite(Integer ... components)
++ {
++ List<AbstractType<?>> types = new ArrayList<>();
++ types.add(Int32Type.instance);
++ types.add(Int32Type.instance);
++ types.add(Int32Type.instance);
++ CompoundDenseCellNameType nameType = new CompoundDenseCellNameType(types);
++ return nameType.make(components);
++ }
++
++ private static List<ByteBuffer> columnNames(Integer ... components)
++ {
++ List<ByteBuffer> names = new ArrayList<>(components.length);
++ for (int component : components)
++ names.add(ByteBufferUtil.bytes(component));
++ return names;
++ }
++}