You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ma...@apache.org on 2024/01/29 15:55:08 UTC
(cassandra) branch cassandra-5.0 updated: Avoid over-skipping of key iterators from static column indexes during mixed intersections
This is an automated email from the ASF dual-hosted git repository.
maedhroz pushed a commit to branch cassandra-5.0
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/cassandra-5.0 by this push:
new 87f2a7b763 Avoid over-skipping of key iterators from static column indexes during mixed intersections
87f2a7b763 is described below
commit 87f2a7b7637a346cdeea23e0b74d9968ccfca619
Author: Caleb Rackliffe <ca...@gmail.com>
AuthorDate: Fri Jan 19 04:39:02 2024 -0600
Avoid over-skipping of key iterators from static column indexes during mixed intersections
patch by Caleb Rackliffe; reviewed by Piotr Kołaczkowski and Alex Petrov for CASSANDRA-19278
Co-authored-by: Caleb Rackliffe <ca...@gmail.com>
Co-authored-by: Piotr Kołaczkowski <pk...@gmail.com>
---
CHANGES.txt | 1 +
.../cassandra/index/sai/disk/RowMapping.java | 49 ++--
.../index/sai/disk/v1/MemtableIndexWriter.java | 31 +--
.../iterators/KeyRangeIntersectionIterator.java | 114 ++++++---
.../index/sai/iterators/KeyRangeIterator.java | 8 +-
.../cassandra/index/sai/utils/PrimaryKey.java | 53 +++-
.../index/sai/cql/RandomIntersectionTest.java | 227 -----------------
.../index/sai/cql/StaticColumnIndexTest.java | 21 +-
.../cql/intersection/RandomIntersectionTester.java | 270 +++++++++++++++++++++
.../intersection/RandomMixedIntersectionTest.java | 53 ++++
.../RandomMixedPartitionIntersectionTest.java | 53 ++++
.../RandomRegularIntersectionTest.java | 53 ++++
.../RandomRegularPartitionIntersectionTest.java | 53 ++++
.../intersection/RandomStaticIntersectionTest.java | 53 ++++
.../RandomStaticPartitionIntersectionTest.java | 53 ++++
.../TwoRegularOneStaticIntersectionTest.java | 53 ++++
...oRegularOneStaticPartitionIntersectionTest.java | 53 ++++
.../cassandra/index/sai/utils/PrimaryKeyTest.java | 3 +-
18 files changed, 897 insertions(+), 304 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 73b0ecdea9..90cb544d10 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
5.0-beta2
+ * Avoid over-skipping of key iterators from static column indexes during mixed intersections (CASSANDRA-19278)
* Make concurrent_index_builders configurable at runtime (CASSANDRA-19266)
* Fix storage_compatibility_mode for streaming (CASSANDRA-19126)
* Add support of vector type to cqlsh COPY command (CASSANDRA-19118)
diff --git a/src/java/org/apache/cassandra/index/sai/disk/RowMapping.java b/src/java/org/apache/cassandra/index/sai/disk/RowMapping.java
index ec122ae006..43bb79b91b 100644
--- a/src/java/org/apache/cassandra/index/sai/disk/RowMapping.java
+++ b/src/java/org/apache/cassandra/index/sai/disk/RowMapping.java
@@ -70,12 +70,6 @@ public class RowMapping
{
return -1;
}
-
- @Override
- public int size()
- {
- return 0;
- }
};
private final InMemoryTrie<Long> rowMapping = new InMemoryTrie<>(BufferType.OFF_HEAP);
@@ -84,10 +78,14 @@ public class RowMapping
public PrimaryKey minKey;
public PrimaryKey maxKey;
+ public PrimaryKey minStaticKey;
+ public PrimaryKey maxStaticKey;
public long maxSSTableRowId = -1;
+ public long maxStaticSSTableRowId = -1;
- public int count;
+ public int rowCount;
+ public int staticRowCount;
private RowMapping()
{}
@@ -171,20 +169,32 @@ public class RowMapping
rowMapping.putSingleton(key, sstableRowId, OVERWRITE_TRANSFORMER);
- maxSSTableRowId = Math.max(maxSSTableRowId, sstableRowId);
-
- // data is written in token sorted order
- if (minKey == null)
- minKey = key;
- maxKey = key;
- count++;
+ // Data is written in primary key order. If a schema contains clustering keys, it may also contain static
+ // columns. We track min, max, and count for static keys separately here so that we can pass them to the segment
+ // metadata for indexes on static columns.
+ if (key.kind() == PrimaryKey.Kind.STATIC)
+ {
+ if (minStaticKey == null)
+ minStaticKey = key;
+ maxStaticKey = key;
+ staticRowCount++;
+ maxStaticSSTableRowId = Math.max(maxStaticSSTableRowId, sstableRowId);
+ }
+ else
+ {
+ if (minKey == null)
+ minKey = key;
+ maxKey = key;
+ rowCount++;
+ maxSSTableRowId = Math.max(maxSSTableRowId, sstableRowId);
+ }
}
/**
- * Returns the SSTable row Id for a {@link PrimaryKey}
+ * Returns the SSTable row ID for a {@link PrimaryKey}
*
* @param key the {@link PrimaryKey}
- * @return a valid SSTable row Id for the {@link PrimaryKey} or -1 if the {@link PrimaryKey} doesn't exist
+ * @return a valid SSTable row ID for the {@link PrimaryKey} or -1 if the {@link PrimaryKey} doesn't exist
* in the {@link RowMapping}
*/
public int get(PrimaryKey key)
@@ -193,13 +203,8 @@ public class RowMapping
return sstableRowId == null ? -1 : Math.toIntExact(sstableRowId);
}
- public int size()
- {
- return count;
- }
-
public boolean hasRows()
{
- return maxSSTableRowId >= 0;
+ return maxSSTableRowId >= 0 || maxStaticSSTableRowId >= 0;
}
}
diff --git a/src/java/org/apache/cassandra/index/sai/disk/v1/MemtableIndexWriter.java b/src/java/org/apache/cassandra/index/sai/disk/v1/MemtableIndexWriter.java
index 65e42ef91e..2bf6639e16 100644
--- a/src/java/org/apache/cassandra/index/sai/disk/v1/MemtableIndexWriter.java
+++ b/src/java/org/apache/cassandra/index/sai/disk/v1/MemtableIndexWriter.java
@@ -155,15 +155,15 @@ public class MemtableIndexWriter implements PerColumnIndexWriter
return 0;
}
+ PrimaryKey minKey = indexTermType.columnMetadata().isStatic() ? rowMapping.minStaticKey : rowMapping.minKey;
+ PrimaryKey maxKey = indexTermType.columnMetadata().isStatic() ? rowMapping.maxStaticKey : rowMapping.maxKey;
+
// During index memtable flush, the data is sorted based on terms.
SegmentMetadata metadata = new SegmentMetadata(0,
numRows,
- terms.getMinSSTableRowId(),
- terms.getMaxSSTableRowId(),
- rowMapping.minKey,
- rowMapping.maxKey,
- terms.getMinTerm(),
- terms.getMaxTerm(),
+ terms.getMinSSTableRowId(), terms.getMaxSSTableRowId(),
+ minKey, maxKey,
+ terms.getMinTerm(), terms.getMaxTerm(),
indexMetas);
try (MetadataWriter metadataWriter = new MetadataWriter(indexDescriptor.openPerIndexOutput(IndexComponent.META, indexIdentifier)))
@@ -176,18 +176,19 @@ public class MemtableIndexWriter implements PerColumnIndexWriter
private void flushVectorIndex(long startTime, Stopwatch stopwatch) throws IOException
{
- SegmentMetadata.ComponentMetadataMap metadataMap = memtable.writeDirect(indexDescriptor, indexIdentifier, rowMapping::get);
+ int rowCount = indexTermType.columnMetadata().isStatic() ? rowMapping.staticRowCount : rowMapping.rowCount;
+ PrimaryKey minKey = indexTermType.columnMetadata().isStatic() ? rowMapping.minStaticKey : rowMapping.minKey;
+ PrimaryKey maxKey = indexTermType.columnMetadata().isStatic() ? rowMapping.maxStaticKey : rowMapping.maxKey;
+ long maxSSTableRowId = indexTermType.columnMetadata().isStatic() ? rowMapping.maxStaticSSTableRowId : rowMapping.maxSSTableRowId;
- completeIndexFlush(rowMapping.size(), startTime, stopwatch);
+ SegmentMetadata.ComponentMetadataMap metadataMap = memtable.writeDirect(indexDescriptor, indexIdentifier, rowMapping::get);
+ completeIndexFlush(rowCount, startTime, stopwatch);
SegmentMetadata metadata = new SegmentMetadata(0,
- rowMapping.size(),
- 0,
- rowMapping.maxSSTableRowId,
- rowMapping.minKey,
- rowMapping.maxKey,
- ByteBufferUtil.bytes(0),
- ByteBufferUtil.bytes(0),
+ rowCount,
+ 0, maxSSTableRowId,
+ minKey, maxKey,
+ ByteBufferUtil.bytes(0), ByteBufferUtil.bytes(0),
metadataMap);
try (MetadataWriter writer = new MetadataWriter(indexDescriptor.openPerIndexOutput(IndexComponent.META, indexIdentifier)))
diff --git a/src/java/org/apache/cassandra/index/sai/iterators/KeyRangeIntersectionIterator.java b/src/java/org/apache/cassandra/index/sai/iterators/KeyRangeIntersectionIterator.java
index 6e297b338c..f674c04dfc 100644
--- a/src/java/org/apache/cassandra/index/sai/iterators/KeyRangeIntersectionIterator.java
+++ b/src/java/org/apache/cassandra/index/sai/iterators/KeyRangeIntersectionIterator.java
@@ -28,13 +28,19 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.CassandraRelevantProperties;
import org.apache.cassandra.index.sai.utils.PrimaryKey;
+import org.apache.cassandra.index.sai.utils.PrimaryKey.Kind;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.tracing.Tracing;
+import javax.annotation.Nullable;
+
/**
* A simple intersection iterator that makes no real attempts at optimising the iteration apart from
- * initially sorting the ranges. This implementation also supports an intersection limit which limits
- * the number of ranges that will be included in the intersection. This currently defaults to 2.
+ * initially sorting the ranges. This implementation also supports an intersection limit via
+ * {@code CassandraRelevantProperties.SAI_INTERSECTION_CLAUSE_LIMIT} which limits the number of ranges that will
+ * be included in the intersection.
+ * <p>
+ * Intersection only works for ranges that are compatible according to {@link PrimaryKey.Kind#isIntersectable(Kind)}.
*/
public class KeyRangeIntersectionIterator extends KeyRangeIterator
{
@@ -56,52 +62,82 @@ public class KeyRangeIntersectionIterator extends KeyRangeIterator
@Override
protected PrimaryKey computeNext()
{
- // Range iterator that has been advanced in the previous cycle of the outer loop.
- // Initially there hasn't been the previous cycle, so set to null.
- int alreadyAvanced = -1;
-
- // The highest primary key seen on any range iterator so far.
+ // Advance one iterator to the next key and remember the key as the highest seen so far.
// It can become null when we reach the end of the iterator.
- PrimaryKey highestKey = getCurrent();
+ // If there are both static and non-static keys being iterated here, we advance a non-static one,
+ // regardless of the order of ranges in the ranges list.
+ PrimaryKey highestKey = advanceOneRange();
outer:
- // We need to check if highestKey exceeds the maximum because the maximum is
- // the lowest value maximum of all the ranges. As a result any value could
- // potentially exceed it.
+ // After advancing one iterator, we must try to advance all the other iterators that got behind,
+ // so they catch up to it. Note that we will not advance the iterators for static columns
+ // as long as they point to the partition of the highest key. (This is because STATIC primary keys
+ // compare to other keys only by partition.) This loop continues until all iterators point to the same key,
+ // or if we run out of keys on any of them, or if we exceed the maximum key.
+ // There is no point in iterating after maximum, because no keys will match beyond that point.
while (highestKey != null && highestKey.compareTo(getMaximum()) <= 0)
{
- // Try advance all iterators to the highest key seen so far.
+ // Try to advance all iterators to the highest key seen so far.
// Once this inner loop finishes normally, all iterators are guaranteed to be at the same value.
- for (int index = 0; index < ranges.size(); index++)
+ for (KeyRangeIterator range : ranges)
{
- if (index != alreadyAvanced)
+ if (range.getCurrent().compareTo(highestKey) < 0)
{
- KeyRangeIterator range = ranges.get(index);
- PrimaryKey nextKey = nextOrNull(range, highestKey);
- if (nextKey == null || nextKey.compareTo(highestKey) > 0)
+ // If we advance a STATIC key, then we must advance it to the same partition as the highestKey.
+ // Advancing a STATIC key to a WIDE key directly (without throwing away the clustering) would
+ // go too far, as WIDE keys are stored after STATIC in the posting list.
+ PrimaryKey nextKey = range.getCurrent().kind() == Kind.STATIC
+ ? nextOrNull(range, highestKey.toStatic())
+ : nextOrNull(range, highestKey);
+
+ // We use strict comparison here, since it orders WIDE primary keys after STATIC primary keys
+ // in the same partition. When WIDE keys are present, we want to return them rather than STATIC
+ // keys to avoid retrieving and post-filtering entire partitions.
+ if (nextKey == null || nextKey.compareToStrict(highestKey) > 0)
{
// We jumped over the highest key seen so far, so make it the new highest key.
highestKey = nextKey;
- // Remember this iterator to avoid advancing it again, because it is already at the highest key
- alreadyAvanced = index;
- // This iterator jumped over, so the other iterators are lagging behind now,
+
+ // This iterator jumped over, so the other iterators might be lagging behind now,
// including the ones already advanced in the earlier cycles of the inner loop.
- // Therefore, restart the inner loop in order to advance
- // the other iterators except this one to match the new highest key.
+ // Therefore, restart the inner loop in order to advance the lagging iterators.
continue outer;
}
- assert nextKey.compareTo(highestKey) == 0:
- String.format("skipped to an item smaller than the target; " +
- "iterator: %s, target key: %s, returned key: %s", range, highestKey, nextKey);
+ assert nextKey.compareTo(highestKey) == 0 :
+ String.format("Skipped to a key smaller than the target! " +
+ "iterator: %s, target key: %s, returned key: %s", range, highestKey, nextKey);
}
}
- // If we reached here, next() has been called at least once on each range iterator and
- // the last call to next() on each iterator returned a value equal to the highestKey.
+
+ // If we get here, all iterators have been advanced to the same key. When STATIC and WIDE keys are
+ // mixed, this means WIDE keys point to exactly the same row, and STATIC keys the same partition.
return highestKey;
}
+
return endOfData();
}
+ /**
+ * Advances the iterator of one range to the next item, which becomes the highest seen so far.
+ * Iterators pointing to STATIC keys are advanced only if no non-STATIC keys have been advanced.
+ *
+ * @return the next highest key or null if the iterator has reached the end
+ */
+ private @Nullable PrimaryKey advanceOneRange()
+ {
+ PrimaryKey highestKey = getCurrent();
+
+ for (KeyRangeIterator range : ranges)
+ if (range.getCurrent().kind() != Kind.STATIC)
+ return nextOrNull(range, highestKey);
+
+ for (KeyRangeIterator range : ranges)
+ if (range.getCurrent().kind() == Kind.STATIC)
+ return nextOrNull(range, highestKey);
+
+ throw new IllegalStateException("There should be at least one range to advance!");
+ }
+
@Override
protected void performSkipTo(PrimaryKey nextKey)
{
@@ -236,6 +272,20 @@ public class KeyRangeIntersectionIterator extends KeyRangeIterator
if (ranges.size() == 1)
return ranges.get(0);
+ // Make sure intersection is supported on the ranges provided:
+ PrimaryKey.Kind firstKind = null;
+
+ for (KeyRangeIterator range : ranges)
+ {
+ PrimaryKey key = range.getCurrent();
+
+ if (key != null)
+ if (firstKind == null)
+ firstKind = key.kind();
+ else if (!firstKind.isIntersectable(key.kind()))
+ throw new IllegalArgumentException("Cannot intersect " + firstKind + " and " + key.kind() + " ranges!");
+ }
+
return new KeyRangeIntersectionIterator(statistics, ranges);
}
@@ -277,18 +327,18 @@ public class KeyRangeIntersectionIterator extends KeyRangeIterator
/**
* Ranges are overlapping the following cases:
- *
+ * <p>
* * When they have a common subrange:
- *
+ * <p>
* min b.current max b.max
* +---------|--------------+------------|
- *
+ * <p>
* b.current min max b.max
* |--------------+---------+------------|
- *
+ * <p>
* min b.current b.max max
* +----------|-------------|------------+
- *
+ * <p>
*
* If either range is empty, they're disjoint.
*/
diff --git a/src/java/org/apache/cassandra/index/sai/iterators/KeyRangeIterator.java b/src/java/org/apache/cassandra/index/sai/iterators/KeyRangeIterator.java
index cb6b69a5a9..da1b8c2a04 100644
--- a/src/java/org/apache/cassandra/index/sai/iterators/KeyRangeIterator.java
+++ b/src/java/org/apache/cassandra/index/sai/iterators/KeyRangeIterator.java
@@ -211,19 +211,19 @@ public abstract class KeyRangeIterator extends AbstractGuavaIterator<PrimaryKey>
}
}
- protected static <T extends Comparable<T>> T nullSafeMin(T a, T b)
+ protected static PrimaryKey nullSafeMin(PrimaryKey a, PrimaryKey b)
{
if (a == null) return b;
if (b == null) return a;
- return a.compareTo(b) > 0 ? b : a;
+ return a.compareToStrict(b) > 0 ? b : a;
}
- protected static <T extends Comparable<T>> T nullSafeMax(T a, T b)
+ protected static PrimaryKey nullSafeMax(PrimaryKey a, PrimaryKey b)
{
if (a == null) return b;
if (b == null) return a;
- return a.compareTo(b) > 0 ? a : b;
+ return a.compareToStrict(b) > 0 ? a : b;
}
}
diff --git a/src/java/org/apache/cassandra/index/sai/utils/PrimaryKey.java b/src/java/org/apache/cassandra/index/sai/utils/PrimaryKey.java
index f3492927eb..c079a66c4c 100644
--- a/src/java/org/apache/cassandra/index/sai/utils/PrimaryKey.java
+++ b/src/java/org/apache/cassandra/index/sai/utils/PrimaryKey.java
@@ -20,7 +20,6 @@ package org.apache.cassandra.index.sai.utils;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Objects;
-
import java.util.stream.Collectors;
import org.apache.cassandra.db.BufferDecoratedKey;
@@ -59,6 +58,18 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
{
this.hasClustering = hasClustering;
}
+
+ public boolean isIntersectable(Kind other)
+ {
+ if (this == TOKEN)
+ return other == TOKEN;
+ else if (this == SKINNY)
+ return other == SKINNY;
+ else if (this == WIDE || this == STATIC)
+ return other == WIDE || other == STATIC;
+
+ throw new AssertionError("Unknown Kind: " + other);
+ }
}
class Factory
@@ -315,6 +326,15 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
return 0;
}
+ @Override
+ public int compareToStrict(PrimaryKey o)
+ {
+ int cmp = compareTo(o);
+ // Always order this STATIC key before a WIDE key in the same partition, as this corresponds to the
+ // order of the corresponding row IDs in an on-disk postings list.
+ return o.kind() == Kind.WIDE && cmp == 0 ? -1 : cmp;
+ }
+
@Override
public int hashCode()
{
@@ -326,6 +346,12 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
{
return String.format("PrimaryKey: { token: %s, partition: %s, clustering: STATIC } ", token(), partitionKey());
}
+
+ @Override
+ public PrimaryKey toStatic()
+ {
+ return this;
+ }
}
class WidePrimaryKey extends SkinnyPrimaryKey
@@ -376,6 +402,15 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
return clusteringComparator.compare(clustering(), o.clustering());
}
+ @Override
+ public int compareToStrict(PrimaryKey o)
+ {
+ int cmp = compareTo(o);
+ // Always order this WIDE key before a STATIC key in the same partition, as this corresponds to the
+ // order of the corresponding row IDs in an on-disk postings list.
+ return o.kind() == Kind.STATIC && cmp == 0 ? 1 : cmp;
+ }
+
@Override
public int hashCode()
{
@@ -393,6 +428,12 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
.map(ByteBufferUtil::bytesToHex)
.collect(Collectors.joining(", ")));
}
+
+ @Override
+ public PrimaryKey toStatic()
+ {
+ return new StaticPrimaryKey(partitionKey);
+ }
}
}
@@ -438,4 +479,14 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
* @throws UnsupportedOperationException for {@link PrimaryKey} implementations that are not byte-comparable
*/
ByteSource asComparableBytes(ByteComparable.Version version);
+
+ default PrimaryKey toStatic()
+ {
+ throw new UnsupportedOperationException("Only STATIC and WIDE keys can be converted to STATIC");
+ }
+
+ default int compareToStrict(PrimaryKey o)
+ {
+ return compareTo(o);
+ }
}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/RandomIntersectionTest.java b/test/unit/org/apache/cassandra/index/sai/cql/RandomIntersectionTest.java
deleted file mode 100644
index 3ab0b80e35..0000000000
--- a/test/unit/org/apache/cassandra/index/sai/cql/RandomIntersectionTest.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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.index.sai.cql;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import org.apache.cassandra.index.sai.utils.SAIRandomizedTester;
-
-@RunWith(Parameterized.class)
-public class RandomIntersectionTest extends SAIRandomizedTester
-{
- private static final Object[][] EMPTY_ROWS = new Object[][]{};
-
- @Parameterized.Parameter
- public String testName;
-
- @Parameterized.Parameter(1)
- public boolean partitionRestricted;
-
- @Parameterized.Parameter(2)
- public boolean largePartition;
-
- @Parameterized.Parameter(3)
- public boolean v1Cardinality;
-
- @Parameterized.Parameter(4)
- public boolean v2Cardinality;
-
- @Parameterized.Parameters(name = "{0}")
- public static List<Object[]> parameters()
- {
- List<Object[]> parameters = new LinkedList<>();
-
- parameters.add(new Object[]{ "Large partition restricted high high", true, true, true, true });
- parameters.add(new Object[]{ "Large partition restricted low low", true, true, false, false });
- parameters.add(new Object[]{ "Large partition restricted high low", true, true, true, false });
- parameters.add(new Object[]{ "Large partition unrestricted high high", false, true, true, true });
- parameters.add(new Object[]{ "Large partition unrestricted low low", false, true, false, false });
- parameters.add(new Object[]{ "Large partition unrestricted high low", false, true, true, false });
- parameters.add(new Object[]{ "Small partition restricted high high", true, false, true, true });
- parameters.add(new Object[]{ "Small partition restricted low low", true, false, false, false });
- parameters.add(new Object[]{ "Small partition restricted high low", true, false, true, false });
- parameters.add(new Object[]{ "Small partition unrestricted high high", false, false, true, true });
- parameters.add(new Object[]{ "Small partition unrestricted low low", false, false, false, false });
- parameters.add(new Object[]{ "Small partition unrestricted high low", false, false, true, false });
-
- return parameters;
- }
-
- private int numRows;
-
- @Before
- public void createTableAndIndexes()
- {
- createTable("CREATE TABLE %s (pk int, ck int, v1 int, v2 int, PRIMARY KEY(pk, ck))");
- createIndex("CREATE INDEX ON %s(v1) USING 'sai'");
- createIndex("CREATE INDEX ON %s(v2) USING 'sai'");
-
- numRows = nextInt(50000, 200000);
- }
-
- @Test
- public void randomIntersectionTest() throws Throwable
- {
- if (partitionRestricted)
- runRestrictedQueries();
- else
- runUnrestrictedQueries();
- }
-
- private void runRestrictedQueries() throws Throwable
- {
- Map<Integer, List<TestRow>> testRowMap = buildAndLoadTestRows();
-
- beforeAndAfterFlush(() -> {
- for (int queryCount = 0; queryCount < nextInt(10, 100); queryCount++)
- {
- int pk = testRowMap.keySet().stream().skip(nextInt(0, testRowMap.size())).findFirst().orElseThrow();
- int v1 = nextV1();
- int v2 = nextV2();
-
- List<Object[]> expected = testRowMap.get(pk)
- .stream()
- .sorted(Comparator.comparingInt(o -> o.ck))
- .filter(row -> row.v1 > v1 && row.v2 > v2)
- .map(row -> row(row.ck))
- .collect(Collectors.toList());
-
- assertRows(execute("SELECT ck FROM %s WHERE pk = ? AND v1 > ? AND v2 > ?", pk, v1, v2), expected.toArray(EMPTY_ROWS));
- }
- });
- }
-
- private void runUnrestrictedQueries() throws Throwable
- {
- Map<Integer, List<TestRow>> testRowMap = buildAndLoadTestRows();
-
- beforeAndAfterFlush(() -> {
- for (int queryCount = 0; queryCount < nextInt(10, 100); queryCount++)
- {
- int v1 = nextV1();
- int v2 = nextV2();
-
- List<Object[]> expected = testRowMap.values()
- .stream()
- .flatMap(Collection::stream)
- .filter(row -> row.v1 == v1 && row.v2 == v2)
- .map(row -> row(row.ck))
- .collect(Collectors.toList());
-
- assertRowsIgnoringOrder(execute("SELECT ck FROM %s WHERE v1 = ? AND v2 = ?", v1, v2), expected.toArray(EMPTY_ROWS));
- }
- });
- }
-
- private Map<Integer, List<TestRow>> buildAndLoadTestRows()
- {
- Map<Integer, List<TestRow>> testRowMap = new HashMap<>();
-
- int clusterSize = largePartition ? nextInt(500, 5000) : nextInt(10, 100);
- int partition = nextInt(0, numRows);
- List<TestRow> rowList = new ArrayList<>(clusterSize);
- testRowMap.put(partition, rowList);
- int clusterCount = 0;
- for (int index = 0; index < numRows; index++)
- {
- TestRow row = new TestRow(partition, nextInt(10, numRows), nextV1(), nextV2());
- while (rowList.contains(row))
- row = new TestRow(partition, nextInt(10, numRows), nextV1(), nextV2());
-
- rowList.add(row);
- clusterCount++;
- if (clusterCount == clusterSize)
- {
- clusterCount = 0;
- clusterSize = largePartition ? nextInt(500, 5000) : nextInt(10, 100);
- partition = nextInt(0, numRows);
- while (testRowMap.containsKey(partition))
- partition = nextInt(0, numRows);
- rowList = new ArrayList<>(clusterSize);
- testRowMap.put(partition, rowList);
- }
- }
- testRowMap.values().stream().flatMap(Collection::stream).forEach(row -> execute("INSERT INTO %s (pk, ck, v1, v2) VALUES (?, ?, ?, ?)",
- row.pk, row.ck, row.v1, row.v2));
- return testRowMap;
- }
-
- private int nextV1()
- {
- return v1Cardinality ? nextInt(10, numRows/10) : nextInt(10, numRows/1000);
- }
-
- private int nextV2()
- {
- return v2Cardinality ? nextInt(10, numRows/10) : nextInt(10, numRows/1000);
- }
-
- private static class TestRow implements Comparable<TestRow>
- {
- final int pk;
- final int ck;
- final int v1;
- final int v2;
-
- TestRow(int pk, int ck, int v1, int v2)
- {
- this.pk = pk;
- this.ck = ck;
- this.v1 = v1;
- this.v2 = v2;
- }
-
- @Override
- public int compareTo(TestRow other)
- {
- int cmp = Integer.compare(pk, other.pk);
- if (cmp != 0)
- return cmp;
- return Integer.compare(ck, other.ck);
- }
-
- @Override
- public boolean equals(Object obj)
- {
- if (obj instanceof TestRow)
- return compareTo((TestRow) obj) == 0;
-
- return false;
- }
-
- @Override
- public int hashCode()
- {
- return Objects.hash(pk, ck);
- }
- }
-}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/StaticColumnIndexTest.java b/test/unit/org/apache/cassandra/index/sai/cql/StaticColumnIndexTest.java
index 0bdee106fd..faaf74f5c6 100644
--- a/test/unit/org/apache/cassandra/index/sai/cql/StaticColumnIndexTest.java
+++ b/test/unit/org/apache/cassandra/index/sai/cql/StaticColumnIndexTest.java
@@ -49,7 +49,26 @@ public class StaticColumnIndexTest extends SAITester
execute("INSERT INTO %s(pk, ck, val2) VALUES(?, ?, ?)", 1, 2, 2000);
execute("INSERT INTO %s(pk, ck, val1, val2) VALUES(?, ?, ?, ?)", 2, 1, 40, 2000);
- beforeAndAfterFlush(() -> assertRows(execute("SELECT pk, ck, val1, val2 FROM %s WHERE val1 = 20 AND val2 = 2000 ALLOW FILTERING"),
+ beforeAndAfterFlush(() -> assertRows(execute("SELECT pk, ck, val1, val2 FROM %s WHERE val1 = 20 AND val2 = 2000"),
row(1, 2, 20, 2000)));
}
+
+ @Test
+ public void staticAndNonStaticRangeIntersection() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk int, ck int, v1 int, s1 int static, PRIMARY KEY(pk, ck))");
+ createIndex("CREATE INDEX ON %s(v1) USING 'sai'");
+ createIndex("CREATE INDEX ON %s(s1) USING 'sai'");
+
+ execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 1, 0);
+ execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 2, 1);
+ execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 3, 2);
+ execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 4, 3);
+ execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 5, 4);
+ execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 6, 5);
+
+ execute("INSERT INTO %s (pk, s1) VALUES (?, ?)", 0, 100);
+
+ beforeAndAfterFlush(() -> assertRowCount(execute("SELECT * FROM %s WHERE pk = ? AND v1 > ? AND s1 = ?", 0, 2, 100), 3));
+ }
}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomIntersectionTester.java b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomIntersectionTester.java
new file mode 100644
index 0000000000..3b1436ae39
--- /dev/null
+++ b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomIntersectionTester.java
@@ -0,0 +1,270 @@
+/*
+ * 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.index.sai.cql.intersection;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.runners.Parameterized;
+
+import org.apache.cassandra.config.CassandraRelevantProperties;
+import org.apache.cassandra.cql3.UntypedResultSet;
+import org.apache.cassandra.index.sai.utils.SAIRandomizedTester;
+
+public abstract class RandomIntersectionTester extends SAIRandomizedTester
+{
+ private static final Object[][] EMPTY_ROWS = new Object[][]{};
+
+ protected enum Mode { REGULAR, STATIC, REGULAR_STATIC, TWO_REGULAR_ONE_STATIC }
+
+ @Parameterized.Parameter
+ public String testName;
+
+ @Parameterized.Parameter(1)
+ public boolean partitionRestricted;
+
+ @Parameterized.Parameter(2)
+ public boolean largePartition;
+
+ @Parameterized.Parameter(3)
+ public boolean v1Cardinality;
+
+ @Parameterized.Parameter(4)
+ public boolean v2Cardinality;
+
+ @Parameterized.Parameter(5)
+ public Mode mode;
+
+ private int numPartitions;
+
+ @Before
+ public void createTableAndIndexes()
+ {
+ CassandraRelevantProperties.SAI_INTERSECTION_CLAUSE_LIMIT.setInt(3);
+
+ createTable("CREATE TABLE %s (pk int, ck int, v1 int, v2 int, s1 int static, s2 int static, PRIMARY KEY(pk, ck))");
+ createIndex("CREATE INDEX ON %s(v1) USING 'sai'");
+ createIndex("CREATE INDEX ON %s(v2) USING 'sai'");
+ createIndex("CREATE INDEX ON %s(s1) USING 'sai'");
+ createIndex("CREATE INDEX ON %s(s2) USING 'sai'");
+
+ numPartitions = nextInt(15000, 100000);
+ }
+
+ protected void runRestrictedQueries() throws Throwable
+ {
+ Map<Integer, List<TestRow>> testRowMap = buildAndLoadTestRows();
+
+ beforeAndAfterFlush(() -> {
+ for (int queryCount = 0; queryCount < nextInt(10, 100); queryCount++)
+ {
+ int pk = testRowMap.keySet().stream().skip(nextInt(0, testRowMap.size())).findFirst().orElseThrow();
+ int v1 = nextV1();
+ int v2 = nextV2();
+
+ Predicate<TestRow> predicate = null;
+
+ if (mode == Mode.REGULAR)
+ predicate = row -> row.v1 > v1 && row.v2 > v2;
+ else if (mode == Mode.STATIC)
+ predicate = row -> row.s1 > v1 && row.s2 > v2;
+ else if (mode == Mode.REGULAR_STATIC)
+ predicate = row -> row.v1 > v1 && row.s2 > v2;
+ else if (mode == Mode.TWO_REGULAR_ONE_STATIC)
+ predicate = row -> row.v1 > v1 && row.v2 > v2 && row.s2 > v2;
+
+ assert predicate != null : "Predicate should be assigned!";
+
+ List<Object[]> expected = testRowMap.get(pk)
+ .stream()
+ .sorted(Comparator.comparingInt(o -> o.ck))
+ .filter(predicate)
+ .map(row -> row(row.pk, row.ck))
+ .collect(Collectors.toList());
+
+ UntypedResultSet result = null;
+
+ if (mode == Mode.REGULAR)
+ result = execute("SELECT pk, ck FROM %s WHERE pk = ? AND v1 > ? AND v2 > ?", pk, v1, v2);
+ else if (mode == Mode.STATIC)
+ result = execute("SELECT pk, ck FROM %s WHERE pk = ? AND s1 > ? AND s2 > ?", pk, v1, v2);
+ else if (mode == Mode.REGULAR_STATIC)
+ result = execute("SELECT pk, ck FROM %s WHERE pk = ? AND v1 > ? AND s2 > ?", pk, v1, v2);
+ else if (mode == Mode.TWO_REGULAR_ONE_STATIC)
+ result = execute("SELECT pk, ck FROM %s WHERE pk = ? AND v1 > ? AND v2 > ? AND s2 > ?", pk, v1, v2, v2);
+
+ assertRows(result, expected.toArray(EMPTY_ROWS));
+ }
+ });
+ }
+
+ protected void runUnrestrictedQueries() throws Throwable
+ {
+ Map<Integer, List<TestRow>> testRowMap = buildAndLoadTestRows();
+
+ beforeAndAfterFlush(() -> {
+ for (int queryCount = 0; queryCount < nextInt(10, 100); queryCount++)
+ {
+ int v1 = nextV1();
+ int v2 = nextV2();
+
+ Predicate<TestRow> predicate = null;
+
+ if (mode == Mode.REGULAR)
+ predicate = row -> row.v1 == v1 && row.v2 > v2;
+ else if (mode == Mode.STATIC)
+ predicate = row -> row.s1 > v1 && row.s2 > v2;
+ else if (mode == Mode.REGULAR_STATIC)
+ predicate = row -> row.v1 == v1 && row.s2 > v2;
+ else if (mode == Mode.TWO_REGULAR_ONE_STATIC)
+ predicate = row -> row.v1 == v1 && row.v2 > v2 && row.s2 > v2;
+
+ assert predicate != null : "Predicate should be assigned!";
+
+ List<Object[]> expected = testRowMap.values()
+ .stream()
+ .flatMap(Collection::stream)
+ .filter(predicate)
+ .map(row -> row(row.pk, row.ck))
+ .collect(Collectors.toList());
+
+ UntypedResultSet result = null;
+
+ if (mode == Mode.REGULAR)
+ result = execute("SELECT pk, ck FROM %s WHERE v1 = ? AND v2 > ?", v1, v2);
+ else if (mode == Mode.STATIC)
+ result = execute("SELECT pk, ck FROM %s WHERE s1 > ? AND s2 > ?", v1, v2);
+ else if (mode == Mode.REGULAR_STATIC)
+ result = execute("SELECT pk, ck FROM %s WHERE v1 = ? AND s2 > ?", v1, v2);
+ else if (mode == Mode.TWO_REGULAR_ONE_STATIC)
+ result = execute("SELECT pk, ck FROM %s WHERE v1 = ? AND v2 > ? AND s2 > ?", v1, v2, v2);
+
+ assertRowsIgnoringOrder(result, expected.toArray(EMPTY_ROWS));
+ }
+ });
+ }
+
+ private Map<Integer, List<TestRow>> buildAndLoadTestRows()
+ {
+ Map<Integer, List<TestRow>> testRowMap = new HashMap<>();
+
+ int clusterSize = nextPartitionSize();
+ int partition = nextInt(0, numPartitions);
+ int s1 = nextV1();
+ int s2 = nextV2();
+ List<TestRow> rowList = new ArrayList<>(clusterSize);
+ testRowMap.put(partition, rowList);
+ int clusterCount = 0;
+
+ for (int index = 0; index < numPartitions; index++)
+ {
+ TestRow row = new TestRow(partition, nextInt(10, numPartitions), nextV1(), nextV2(), s1, s2);
+ while (rowList.contains(row))
+ row = new TestRow(partition, nextInt(10, numPartitions), nextV1(), nextV2(), s1, s2);
+
+ rowList.add(row);
+ clusterCount++;
+
+ if (clusterCount == clusterSize)
+ {
+ clusterCount = 0;
+ clusterSize = nextPartitionSize();
+ partition = nextInt(0, numPartitions);
+ while (testRowMap.containsKey(partition))
+ partition = nextInt(0, numPartitions);
+ rowList = new ArrayList<>(clusterSize);
+ testRowMap.put(partition, rowList);
+ }
+ }
+
+ testRowMap.values().stream().flatMap(Collection::stream).forEach(row -> {
+ execute("INSERT INTO %s (pk, ck, v1, v2) VALUES (?, ?, ?, ?)", row.pk, row.ck, row.v1, row.v2);
+ execute("INSERT INTO %s (pk, s1, s2) VALUES (?, ?, ?)", row.pk, row.s1, row.s2);
+ });
+
+ return testRowMap;
+ }
+
+ private int nextPartitionSize()
+ {
+ return largePartition ? nextInt(1024, 4096) : nextInt(1, 64);
+ }
+
+ private int nextV1()
+ {
+ return v1Cardinality ? nextInt(10, numPartitions / 10) : nextInt(10, numPartitions / 1000);
+ }
+
+ private int nextV2()
+ {
+ return v2Cardinality ? nextInt(10, numPartitions / 10) : nextInt(10, numPartitions / 1000);
+ }
+
+ private static class TestRow implements Comparable<TestRow>
+ {
+ final int pk;
+ final int ck;
+ final int v1;
+ final int v2;
+ final int s1;
+ final int s2;
+
+ TestRow(int pk, int ck, int v1, int v2, int s1, int s2)
+ {
+ this.pk = pk;
+ this.ck = ck;
+ this.v1 = v1;
+ this.v2 = v2;
+ this.s1 = s1;
+ this.s2 = s2;
+ }
+
+ @Override
+ public int compareTo(TestRow other)
+ {
+ int cmp = Integer.compare(pk, other.pk);
+ if (cmp != 0)
+ return cmp;
+ return Integer.compare(ck, other.ck);
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof TestRow)
+ return compareTo((TestRow) obj) == 0;
+
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(pk, ck);
+ }
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomMixedIntersectionTest.java b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomMixedIntersectionTest.java
new file mode 100644
index 0000000000..4260ab96a8
--- /dev/null
+++ b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomMixedIntersectionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.index.sai.cql.intersection;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.REGULAR_STATIC;
+
+@RunWith(Parameterized.class)
+public class RandomMixedIntersectionTest extends RandomIntersectionTester
+{
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> parameters()
+ {
+ List<Object[]> parameters = new LinkedList<>();
+
+ parameters.add(new Object[] { "Large partition unrestricted, high, high", false, true, true, true, REGULAR_STATIC});
+ parameters.add(new Object[] { "Large partition unrestricted, low, low", false, true, false, false, REGULAR_STATIC});
+ parameters.add(new Object[] { "Large partition unrestricted, high, low", false, true, true, false, REGULAR_STATIC});
+ parameters.add(new Object[] { "Small partition unrestricted, high, high", false, false, true, true, REGULAR_STATIC});
+ parameters.add(new Object[] { "Small partition unrestricted, low, low", false, false, false, false, REGULAR_STATIC});
+ parameters.add(new Object[] { "Small partition unrestricted, high, low", false, false, true, false, REGULAR_STATIC});
+
+ return parameters;
+ }
+
+ @Test
+ public void testMixedIntersection() throws Throwable
+ {
+ runUnrestrictedQueries();
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomMixedPartitionIntersectionTest.java b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomMixedPartitionIntersectionTest.java
new file mode 100644
index 0000000000..dc12e927fa
--- /dev/null
+++ b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomMixedPartitionIntersectionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.index.sai.cql.intersection;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.REGULAR_STATIC;
+
+@RunWith(Parameterized.class)
+public class RandomMixedPartitionIntersectionTest extends RandomIntersectionTester
+{
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> parameters()
+ {
+ List<Object[]> parameters = new LinkedList<>();
+
+ parameters.add(new Object[] { "Large partition restricted, high, high", true, true, true, true, REGULAR_STATIC});
+ parameters.add(new Object[] { "Large partition restricted, low, low", true, true, false, false, REGULAR_STATIC});
+ parameters.add(new Object[] { "Large partition restricted, high, low", true, true, true, false, REGULAR_STATIC});
+ parameters.add(new Object[] { "Small partition restricted, high, high", true, false, true, true, REGULAR_STATIC});
+ parameters.add(new Object[] { "Small partition restricted, low, low", true, false, false, false, REGULAR_STATIC});
+ parameters.add(new Object[] { "Small partition restricted, high, low", true, false, true, false, REGULAR_STATIC});
+
+ return parameters;
+ }
+
+ @Test
+ public void testMixedIntersection() throws Throwable
+ {
+ runRestrictedQueries();
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomRegularIntersectionTest.java b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomRegularIntersectionTest.java
new file mode 100644
index 0000000000..f05e301103
--- /dev/null
+++ b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomRegularIntersectionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.index.sai.cql.intersection;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.REGULAR;
+
+@RunWith(Parameterized.class)
+public class RandomRegularIntersectionTest extends RandomIntersectionTester
+{
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> parameters()
+ {
+ List<Object[]> parameters = new LinkedList<>();
+
+ parameters.add(new Object[] { "Large partition unrestricted, high, high", false, true, true, true, REGULAR });
+ parameters.add(new Object[] { "Large partition unrestricted, low, low", false, true, false, false, REGULAR });
+ parameters.add(new Object[] { "Large partition unrestricted, high, low", false, true, true, false, REGULAR });
+ parameters.add(new Object[] { "Small partition unrestricted, high, high", false, false, true, true, REGULAR });
+ parameters.add(new Object[] { "Small partition unrestricted, low, low", false, false, false, false, REGULAR });
+ parameters.add(new Object[] { "Small partition unrestricted, high, low", false, false, true, false, REGULAR });
+
+ return parameters;
+ }
+
+ @Test
+ public void testRegularIntersection() throws Throwable
+ {
+ runUnrestrictedQueries();
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomRegularPartitionIntersectionTest.java b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomRegularPartitionIntersectionTest.java
new file mode 100644
index 0000000000..b14fefe743
--- /dev/null
+++ b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomRegularPartitionIntersectionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.index.sai.cql.intersection;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.REGULAR;
+
+@RunWith(Parameterized.class)
+public class RandomRegularPartitionIntersectionTest extends RandomIntersectionTester
+{
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> parameters()
+ {
+ List<Object[]> parameters = new LinkedList<>();
+
+ parameters.add(new Object[] { "Large partition restricted, high, high", true, true, true, true, REGULAR });
+ parameters.add(new Object[] { "Large partition restricted, low, low", true, true, false, false, REGULAR });
+ parameters.add(new Object[] { "Large partition restricted, high, low", true, true, true, false, REGULAR });
+ parameters.add(new Object[] { "Small partition restricted, high, high", true, false, true, true, REGULAR });
+ parameters.add(new Object[] { "Small partition restricted, low, low", true, false, false, false, REGULAR });
+ parameters.add(new Object[] { "Small partition restricted, high, low", true, false, true, false, REGULAR });
+
+ return parameters;
+ }
+
+ @Test
+ public void testRegularIntersection() throws Throwable
+ {
+ runRestrictedQueries();
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomStaticIntersectionTest.java b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomStaticIntersectionTest.java
new file mode 100644
index 0000000000..a1c31eaddc
--- /dev/null
+++ b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomStaticIntersectionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.index.sai.cql.intersection;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.STATIC;
+
+@RunWith(Parameterized.class)
+public class RandomStaticIntersectionTest extends RandomIntersectionTester
+{
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> parameters()
+ {
+ List<Object[]> parameters = new LinkedList<>();
+
+ parameters.add(new Object[] { "Large partition unrestricted, high, high", false, true, true, true, STATIC });
+ parameters.add(new Object[] { "Large partition unrestricted, low, low", false, true, false, false, STATIC });
+ parameters.add(new Object[] { "Large partition unrestricted, high, low", false, true, true, false, STATIC });
+ parameters.add(new Object[] { "Small partition unrestricted, high, high", false, false, true, true, STATIC });
+ parameters.add(new Object[] { "Small partition unrestricted, low, low", false, false, false, false, STATIC });
+ parameters.add(new Object[] { "Small partition unrestricted, high, low", false, false, true, false, STATIC });
+
+ return parameters;
+ }
+
+ @Test
+ public void testStaticIntersection() throws Throwable
+ {
+ runUnrestrictedQueries();
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomStaticPartitionIntersectionTest.java b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomStaticPartitionIntersectionTest.java
new file mode 100644
index 0000000000..b5e458ecfb
--- /dev/null
+++ b/test/unit/org/apache/cassandra/index/sai/cql/intersection/RandomStaticPartitionIntersectionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.index.sai.cql.intersection;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.STATIC;
+
+@RunWith(Parameterized.class)
+public class RandomStaticPartitionIntersectionTest extends RandomIntersectionTester
+{
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> parameters()
+ {
+ List<Object[]> parameters = new LinkedList<>();
+
+ parameters.add(new Object[] { "Large partition restricted, high, high", true, true, true, true, STATIC });
+ parameters.add(new Object[] { "Large partition restricted, low, low", true, true, false, false, STATIC });
+ parameters.add(new Object[] { "Large partition restricted, high, low", true, true, true, false, STATIC });
+ parameters.add(new Object[] { "Small partition restricted, high, high", true, false, true, true, STATIC });
+ parameters.add(new Object[] { "Small partition restricted, low, low", true, false, false, false, STATIC });
+ parameters.add(new Object[] { "Small partition restricted, high, low", true, false, true, false, STATIC });
+
+ return parameters;
+ }
+
+ @Test
+ public void testStaticIntersection() throws Throwable
+ {
+ runRestrictedQueries();
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/intersection/TwoRegularOneStaticIntersectionTest.java b/test/unit/org/apache/cassandra/index/sai/cql/intersection/TwoRegularOneStaticIntersectionTest.java
new file mode 100644
index 0000000000..ddf4551e5e
--- /dev/null
+++ b/test/unit/org/apache/cassandra/index/sai/cql/intersection/TwoRegularOneStaticIntersectionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.index.sai.cql.intersection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.TWO_REGULAR_ONE_STATIC;
+
+@RunWith(Parameterized.class)
+public class TwoRegularOneStaticIntersectionTest extends RandomIntersectionTester
+{
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> parameters()
+ {
+ List<Object[]> parameters = new LinkedList<>();
+
+ parameters.add(new Object[] { "Large partition unrestricted, high, high", false, true, true, true, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Large partition unrestricted, low, low", false, true, false, false, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Large partition unrestricted, high, low", false, true, true, false, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Small partition unrestricted, high, high", false, false, true, true, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Small partition unrestricted, low, low", false, false, false, false, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Small partition unrestricted, high, low", false, false, true, false, TWO_REGULAR_ONE_STATIC});
+
+ return parameters;
+ }
+
+ @Test
+ public void testMixedIntersection() throws Throwable
+ {
+ runUnrestrictedQueries();
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/intersection/TwoRegularOneStaticPartitionIntersectionTest.java b/test/unit/org/apache/cassandra/index/sai/cql/intersection/TwoRegularOneStaticPartitionIntersectionTest.java
new file mode 100644
index 0000000000..2bfc7438a3
--- /dev/null
+++ b/test/unit/org/apache/cassandra/index/sai/cql/intersection/TwoRegularOneStaticPartitionIntersectionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.index.sai.cql.intersection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.TWO_REGULAR_ONE_STATIC;
+
+@RunWith(Parameterized.class)
+public class TwoRegularOneStaticPartitionIntersectionTest extends RandomIntersectionTester
+{
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> parameters()
+ {
+ List<Object[]> parameters = new LinkedList<>();
+
+ parameters.add(new Object[] { "Large partition restricted, high, high", true, true, true, true, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Large partition restricted, low, low", true, true, false, false, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Large partition restricted, high, low", true, true, true, false, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Small partition restricted, high, high", true, false, true, true, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Small partition restricted, low, low", true, false, false, false, TWO_REGULAR_ONE_STATIC});
+ parameters.add(new Object[] { "Small partition restricted, high, low", true, false, true, false, TWO_REGULAR_ONE_STATIC});
+
+ return parameters;
+ }
+
+ @Test
+ public void testMixedIntersection() throws Throwable
+ {
+ runRestrictedQueries();
+ }
+}
diff --git a/test/unit/org/apache/cassandra/index/sai/utils/PrimaryKeyTest.java b/test/unit/org/apache/cassandra/index/sai/utils/PrimaryKeyTest.java
index 1c4b7d40ba..51edc67389 100644
--- a/test/unit/org/apache/cassandra/index/sai/utils/PrimaryKeyTest.java
+++ b/test/unit/org/apache/cassandra/index/sai/utils/PrimaryKeyTest.java
@@ -83,8 +83,7 @@ public class PrimaryKeyTest extends AbstractPrimaryKeyTester
public void simplePartitonStaticAndSingleClusteringAscTest()
{
PrimaryKey.Factory factory = new PrimaryKey.Factory(Murmur3Partitioner.instance, simplePartitionStaticAndSingleClusteringAsc.comparator);
-// int rows = nextInt(10, 100);
- int rows = 10;
+ int rows = nextInt(10, 100);
PrimaryKey[] keys = new PrimaryKey[rows];
int partition = 0;
int clustering = 0;
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org