You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2016/08/16 13:53:01 UTC
[01/12] cassandra git commit: Fix queries with empty ByteBuffer
values in clustering column restrictions
Repository: cassandra
Updated Branches:
refs/heads/trunk e22170e99 -> d1bd71c4e
Fix queries with empty ByteBuffer values in clustering column restrictions
patch by Benjamin Lerer; reviewed by jason Brown and Tyler Hobbs for CASSANDRA-12127
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/527d1897
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/527d1897
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/527d1897
Branch: refs/heads/trunk
Commit: 527d1897c0973491bb0db098ce5ea1e0bd78898f
Parents: bd66547
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 16 15:17:59 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 16 15:17:59 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 10 +
.../cql3/statements/SelectStatement.java | 90 ++++-
.../cql3/statements/UpdateStatement.java | 2 +-
.../cassandra/db/compaction/Scrubber.java | 157 ++++++--
.../cassandra/db/marshal/ReversedType.java | 10 -
.../validation/entities/SecondaryIndexTest.java | 142 ++++++-
.../cql3/validation/operations/DeleteTest.java | 42 +++
.../cql3/validation/operations/SelectTest.java | 373 +++++++++++++++++++
.../cassandra/db/marshal/ReversedTypeTest.java | 4 +-
10 files changed, 782 insertions(+), 49 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 1275631..dee669a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.1.16
+ * Fix queries with empty ByteBuffer values in clustering column restrictions (CASSANDRA-12127)
* Disable passing control to post-flush after flush failure to prevent data loss (CASSANDRA-11828)
* Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
* cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index 7f1b54a..6a70adc 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -13,6 +13,16 @@ restore snapshots created with the previous major version using the
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
+2.1.16
+======
+
+Upgrading
+---------
+ - The ReversedType behaviour has been corrected for clustering columns of
+ BYTES type containing empty value. Scrub should be run on the existing
+ SSTables containing a descending clustering column of BYTES type to correct
+ their ordering. See CASSANDRA-12127 for details.
+
2.1.15
======
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/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 245e64e..40f3f33 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -30,6 +30,7 @@ import com.google.common.collect.Iterators;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.statements.Restriction.Slice;
import org.apache.cassandra.cql3.statements.SingleColumnRestriction.Contains;
import org.apache.cassandra.db.composites.*;
import org.apache.cassandra.transport.messages.ResultMessage;
@@ -246,6 +247,9 @@ public class SelectStatement implements CQLStatement
private Pageable getPageableCommand(QueryOptions options, int limit, long now) throws RequestValidationException
{
+ if (isNotReturningAnyRows(options))
+ return null;
+
int limitForQuery = updateLimitForQuery(limit);
if (isKeyRange || usesSecondaryIndexing)
return getRangeCommand(options, limitForQuery, now);
@@ -254,6 +258,47 @@ public class SelectStatement implements CQLStatement
return commands == null ? null : new Pageable.ReadCommands(commands, limitForQuery);
}
+ /**
+ * Checks if the query will never return any rows.
+ *
+ * @param options the query options
+ * @return {@code true} if the query will never return any rows, {@false} otherwise
+ * @throws InvalidRequestException if the request is invalid
+ */
+ private boolean isNotReturningAnyRows(QueryOptions options) throws InvalidRequestException
+ {
+ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that:
+ // - any query with an EQ restriction containing an empty value will not return any result
+ // - any query with a slice restriction with an empty value for the END bound will not return any result
+
+ if (cfm.comparator.isDense() && !cfm.comparator.isCompound())
+ {
+ for (Restriction restriction : columnRestrictions)
+ {
+ if (restriction != null)
+ {
+ if (restriction.isEQ())
+ {
+ for (ByteBuffer value : restriction.values(options))
+ {
+ if (!value.hasRemaining())
+ return true;
+ }
+ }
+ else if (restriction.isSlice() && ((Slice) restriction).hasBound(Bound.END))
+ {
+ ByteBuffer value = restriction.isMultiColumn()
+ ? ((MultiColumnRestriction.Slice) restriction).componentBounds(Bound.END, options).get(0)
+ : ((Slice) restriction).bound(Bound.END, options);
+
+ return !value.hasRemaining();
+ }
+ }
+ }
+ }
+ return false;
+ }
+
public Pageable getPageableCommand(QueryOptions options) throws RequestValidationException
{
return getPageableCommand(options, getLimit(options), System.currentTimeMillis());
@@ -820,6 +865,9 @@ public class SelectStatement implements CQLStatement
if (val == null)
throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
+ if (ignoreInValue(val))
+ continue;
+
Composite prefix = builder.buildWith(val);
columns.addAll(addSelectedColumns(prefix));
}
@@ -836,6 +884,9 @@ public class SelectStatement implements CQLStatement
throw new InvalidRequestException("Invalid null value in condition for column "
+ cfm.clusteringColumns().get(i + def.position()).name);
+ if (ignoreInValue(components))
+ continue;
+
Composite prefix = builder.buildWith(components);
inValues.addAll(addSelectedColumns(prefix));
}
@@ -846,6 +897,32 @@ public class SelectStatement implements CQLStatement
return addSelectedColumns(builder.build());
}
+ /**
+ * Checks if we should ignore the specified IN value for a clustering column as it will not return any result.
+ *
+ * @param val the IN value to check
+ * @return {@code true} if we should ignore the value, {@code false} otherwise.
+ */
+ private boolean ignoreInValue(ByteBuffer val)
+ {
+ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that we can
+ // ignore any IN value which is an empty byte buffer an which otherwise will trigger an error.
+ return !cfm.comparator.isCompound() && !val.hasRemaining();
+ }
+
+ /**
+ * Checks if we should ignore the specified IN components for a clustering column as it will not return any result.
+ *
+ * @param components the IN components to check
+ * @return {@code true} if we should ignore the value, {@code false} otherwise.
+ */
+ private boolean ignoreInValue(List<ByteBuffer> components)
+ {
+ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that we can
+ // ignore any IN value which is an empty byte buffer an which otherwise will trigger an error.
+ return !cfm.comparator.isCompound() && !components.get(0).hasRemaining();
+ }
+
private SortedSet<CellName> addSelectedColumns(Composite prefix)
{
if (cfm.comparator.isDense())
@@ -1200,12 +1277,17 @@ public class SelectStatement implements CQLStatement
if (sliceRestriction.isInclusive(bound))
return null;
- if (sliceRestriction.isMultiColumn())
- return type.makeCellName(((MultiColumnRestriction.Slice) sliceRestriction).componentBounds(bound, options).toArray());
- else
- return type.makeCellName(sliceRestriction.bound(bound, options));
+ // We can only reach that point if cfm.comparator.isCompound() = false and the table has some clustering columns.
+ // By consequence, we know that the table is a COMPACT table with only one clustering column.
+ ByteBuffer value = sliceRestriction.isMultiColumn() ? ((MultiColumnRestriction.Slice) sliceRestriction).componentBounds(bound, options).get(0)
+ : sliceRestriction.bound(bound, options);
+
+ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, if the slice value is empty
+ // we know that we can treat the slice as inclusive.
+ return value.hasRemaining() ? type.makeCellName(value) : null;
}
+
private Iterator<Cell> applySliceRestriction(final Iterator<Cell> cells, final QueryOptions options) throws InvalidRequestException
{
assert sliceRestriction != null;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
index bf9a059..39e632a 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@ -84,7 +84,7 @@ public class UpdateStatement extends ModificationStatement
if (cfm.comparator.isDense())
{
if (prefix.isEmpty())
- throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().iterator().next()));
+ throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().iterator().next().name));
// An empty name for the compact value is what we use to recognize the case where there is not column
// outside the PK, see CreateStatement.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/compaction/Scrubber.java b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
index 8bfd37b..2df3665 100644
--- a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
+++ b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
@@ -23,9 +23,11 @@ import java.util.*;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
+import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Sets;
import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.columniterator.OnDiskAtomIterator;
import org.apache.cassandra.io.sstable.*;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.io.util.RandomAccessReader;
@@ -223,20 +225,8 @@ public class Scrubber implements Closeable
if (indexFile != null && dataSize != dataSizeFromIndex)
outputHandler.warn(String.format("Data file row size %d different from index file row size %d", dataSize, dataSizeFromIndex));
- SSTableIdentityIterator atoms = new SSTableIdentityIterator(sstable, dataFile, key, dataSize, validateColumns);
- if (prevKey != null && prevKey.compareTo(key) > 0)
- {
- saveOutOfOrderRow(prevKey, key, atoms);
- continue;
- }
-
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
-
- prevKey = key;
+ if (tryAppend(prevKey, key, dataSize, writer))
+ prevKey = key;
}
catch (Throwable th)
{
@@ -252,21 +242,8 @@ public class Scrubber implements Closeable
try
{
dataFile.seek(dataStartFromIndex);
-
- SSTableIdentityIterator atoms = new SSTableIdentityIterator(sstable, dataFile, key, dataSize, validateColumns);
- if (prevKey != null && prevKey.compareTo(key) > 0)
- {
- saveOutOfOrderRow(prevKey, key, atoms);
- continue;
- }
-
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
-
- prevKey = key;
+ if (tryAppend(prevKey, key, dataSize, writer))
+ prevKey = key;
}
catch (Throwable th2)
{
@@ -339,6 +316,32 @@ public class Scrubber implements Closeable
}
}
+ @SuppressWarnings("resource")
+ private boolean tryAppend(DecoratedKey prevKey, DecoratedKey key, long dataSize, SSTableRewriter writer)
+ {
+ // OrderCheckerIterator will check, at iteration time, that the cells are in the proper order. If it detects
+ // that one cell is out of order, it will stop returning them. The remaining cells will be sorted and added
+ // to the outOfOrderRows that will be later written to a new SSTable.
+ OrderCheckerIterator atoms = new OrderCheckerIterator(new SSTableIdentityIterator(sstable, dataFile, key, dataSize, validateColumns),
+ cfs.metadata.comparator.onDiskAtomComparator());
+ if (prevKey != null && prevKey.compareTo(key) > 0)
+ {
+ saveOutOfOrderRow(prevKey, key, atoms);
+ return false;
+ }
+
+ AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
+ if (writer.tryAppend(compactedRow) == null)
+ emptyRows++;
+ else
+ goodRows++;
+
+ if (atoms.hasOutOfOrderCells())
+ saveOutOfOrderRow(key, atoms);
+
+ return true;
+ }
+
private void updateIndexKey()
{
currentIndexKey = nextIndexKey;
@@ -385,12 +388,12 @@ public class Scrubber implements Closeable
}
}
- private void saveOutOfOrderRow(DecoratedKey prevKey, DecoratedKey key, SSTableIdentityIterator atoms)
+ private void saveOutOfOrderRow(DecoratedKey prevKey, DecoratedKey key, OnDiskAtomIterator atoms)
{
saveOutOfOrderRow(key, atoms, String.format("Out of order row detected (%s found after %s)", key, prevKey));
}
- void saveOutOfOrderRow(DecoratedKey key, SSTableIdentityIterator atoms, String message)
+ void saveOutOfOrderRow(DecoratedKey key, OnDiskAtomIterator atoms, String message)
{
// TODO bitch if the row is too large? if it is there's not much we can do ...
outputHandler.warn(message);
@@ -405,6 +408,12 @@ public class Scrubber implements Closeable
outOfOrderRows.add(new Row(key, cf));
}
+ void saveOutOfOrderRow(DecoratedKey key, OrderCheckerIterator atoms)
+ {
+ outputHandler.warn(String.format("Out of order cells found at key %s", key));
+ outOfOrderRows.add(new Row(key, atoms.getOutOfOrderCells()));
+ }
+
public SSTableReader getNewSSTable()
{
return newSstable;
@@ -506,4 +515,90 @@ public class Scrubber implements Closeable
this.emptyRows = scrubber.emptyRows;
}
}
+
+ /**
+ * In some case like CASSANDRA-12127 the cells might have been stored in the wrong order. This decorator check the
+ * cells order and collect the out of order cells to correct the problem.
+ */
+ private static final class OrderCheckerIterator extends AbstractIterator<OnDiskAtom> implements OnDiskAtomIterator
+ {
+ /**
+ * The decorated iterator.
+ */
+ private final OnDiskAtomIterator iterator;
+
+ /**
+ * The atom comparator.
+ */
+ private final Comparator<OnDiskAtom> comparator;
+
+ /**
+ * The Column family containing the cells which are out of order.
+ */
+ private ColumnFamily outOfOrderCells;
+
+ /**
+ * The previous atom returned
+ */
+ private OnDiskAtom previous;
+
+ public OrderCheckerIterator(OnDiskAtomIterator iterator, Comparator<OnDiskAtom> comparator)
+ {
+ this.iterator = iterator;
+ this.comparator = comparator;
+ }
+
+ public ColumnFamily getColumnFamily()
+ {
+ return iterator.getColumnFamily();
+ }
+
+ public DecoratedKey getKey()
+ {
+ return iterator.getKey();
+ }
+
+ public void close() throws IOException
+ {
+ iterator.close();
+ }
+
+ @Override
+ protected OnDiskAtom computeNext()
+ {
+ if (!iterator.hasNext())
+ return endOfData();
+
+ OnDiskAtom next = iterator.next();
+
+ // If we detect that some cells are out of order we will store and sort the remaining once to insert them
+ // in a separate SSTable.
+ if (previous != null && comparator.compare(next, previous) < 0)
+ {
+ outOfOrderCells = collectOutOfOrderCells(next, iterator);
+ return endOfData();
+ }
+ previous = next;
+ return next;
+ }
+
+ public boolean hasOutOfOrderCells()
+ {
+ return outOfOrderCells != null;
+ }
+
+ public ColumnFamily getOutOfOrderCells()
+ {
+ return outOfOrderCells;
+ }
+
+ private static ColumnFamily collectOutOfOrderCells(OnDiskAtom atom, OnDiskAtomIterator iterator)
+ {
+ ColumnFamily cf = iterator.getColumnFamily().cloneMeShallow(ArrayBackedSortedColumns.factory, false);
+ cf.addAtom(atom);
+ while (iterator.hasNext())
+ cf.addAtom(iterator.next());
+ return cf;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/src/java/org/apache/cassandra/db/marshal/ReversedType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/ReversedType.java b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
index 0389a32..53798f8 100644
--- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
@@ -60,16 +60,6 @@ public class ReversedType<T> extends AbstractType<T>
public int compare(ByteBuffer o1, ByteBuffer o2)
{
- // An empty byte buffer is always smaller
- if (o1.remaining() == 0)
- {
- return o2.remaining() == 0 ? 0 : -1;
- }
- if (o2.remaining() == 0)
- {
- return 1;
- }
-
return baseType.compare(o2, o1);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
index a433d06..4a54a9a 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
@@ -34,6 +34,8 @@ import org.apache.cassandra.utils.FBUtilities;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
public class SecondaryIndexTest extends CQLTester
{
@@ -562,7 +564,7 @@ public class SecondaryIndexTest extends CQLTester
{
createTable("CREATE TABLE %s(a int, b frozen<map<int, blob>>, PRIMARY KEY (a))");
createIndex("CREATE INDEX ON %s(full(b))");
- Map<Integer, ByteBuffer> map = new HashMap();
+ Map<Integer, ByteBuffer> map = new HashMap<>();
map.put(0, ByteBuffer.allocate(1024 * 65));
failInsert("INSERT INTO %s (a, b) VALUES (0, ?)", map);
}
@@ -641,4 +643,142 @@ public class SecondaryIndexTest extends CQLTester
assertInvalid("CREATE INDEX ON %s (c)");
}
+ @Test
+ public void testWithEmptyRestrictionValueAndSecondaryIndex() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))");
+ createIndex("CREATE INDEX on %s(c)");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("1"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob('')) AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"),
+ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithSecondaryIndexAndCompactTables() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c)) WITH COMPACT STORAGE");
+ assertInvalidMessage("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables",
+ "CREATE INDEX on %s(c)");
+
+ createTable("CREATE TABLE %s (pk blob PRIMARY KEY, v blob) WITH COMPACT STORAGE");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo123"), bytes("1"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo124"), EMPTY_BYTE_BUFFER);
+
+ assertRows(execute("SELECT * FROM %s WHERE v = textAsBlob('');"),
+ row(bytes("foo124"), EMPTY_BYTE_BUFFER));
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
index 476ec83..6bd5f26 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
@@ -23,11 +23,15 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+
import org.junit.Test;
import org.apache.cassandra.cql3.CQLTester;
import static org.junit.Assert.assertEquals;
+import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
public class DeleteTest extends CQLTester
{
@@ -326,4 +330,42 @@ public class DeleteTest extends CQLTester
assertEmpty(execute("select * from %s where a=1 and b=1"));
}
+
+ @Test
+ public void testDeleteWithEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY (pk, c))" + options);
+
+ if (StringUtils.isEmpty(options))
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+ }
+ else
+ {
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('')");
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))");
+ }
+ }
+ }
+
+ @Test
+ public void testDeleteWithMultipleClusteringColumnsAndEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index 68cf6f8..cef4635 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@ -32,6 +32,8 @@ import org.apache.cassandra.exceptions.InvalidRequestException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
/**
* Test column ranges and ordering with static column in table
@@ -1328,4 +1330,375 @@ public class SelectTest extends CQLTester
row(1, 2, 3),
row(1, 1, 3));
}
+
+ @Test
+ public void testEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk = textAsBlob('');");
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertInvalidMessage("Key may not be empty",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ EMPTY_BYTE_BUFFER, bytes("2"), bytes("2"));
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('');"));
+ }
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Missing PRIMARY KEY part c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ // Test restrictions on non-primary key value
+ assertInvalidMessage("Predicates on the non-primary-key column (v) of a COMPACT table are not yet supported",
+ "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('');"));
+
+ // Test restrictions on non-primary key value
+ assertInvalidMessage("No secondary indexes on the restricted columns support the provided operators",
+ "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumns() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob('1'), textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob('1'), textAsBlob(''));"));
+ }
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) < (textAsBlob(''), textAsBlob('1'));"));
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("1"),
+ bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("2"),
+ bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause));
+
+ }
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Missing PRIMARY KEY part c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ EMPTY_BYTE_BUFFER,
+ bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumnsAndOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c1 DESC, c2 DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c1 DESC, c2 DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c1 DESC, c2 DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+ }
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+ }
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/527d1897/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java b/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java
index 1553a20..7dfbc27 100644
--- a/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java
+++ b/test/unit/org/apache/cassandra/db/marshal/ReversedTypeTest.java
@@ -34,7 +34,7 @@ public class ReversedTypeTest
assert t.compare(bytes(4L), bytes(2L)) < 0;
// the empty byte buffer is always the smaller
- assert t.compare(EMPTY_BYTE_BUFFER, bytes(2L)) < 0;
- assert t.compare(bytes(2L), EMPTY_BYTE_BUFFER) > 0;
+ assert t.compare(EMPTY_BYTE_BUFFER, bytes(2L)) > 0;
+ assert t.compare(bytes(2L), EMPTY_BYTE_BUFFER) < 0;
}
}
[08/12] cassandra git commit: Merge branch cassandra-2.2 into
cassandra-3.0
Posted by bl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
index 6e06419,32d800a..bb35458
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
@@@ -36,31 -36,37 +36,33 @@@ public class SelectOrderByTest extends
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 2, 2);
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0, 0, 0),
- row(0, 1, 1),
- row(0, 2, 2)
- );
-
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(0, 2, 2),
- row(0, 1, 1),
- row(0, 0, 0)
- );
-
- // order by the only column in the selection
- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
-
- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
-
- // order by a column not in the selection
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0, 0, 0),
+ row(0, 1, 1),
+ row(0, 2, 2)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(0, 2, 2),
+ row(0, 1, 1),
+ row(0, 0, 0)
+ );
+
+ // order by the only column in the selection
+ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2));
+
+ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
+
+ // order by a column not in the selection
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2));
+
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
- }
++ });
}
}
@@@ -74,22 -80,28 +76,24 @@@
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 2, 2);
- // order by the only column in the selection
- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ // order by the only column in the selection
+ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2));
- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
- // order by a column not in the selection
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
+ // order by a column not in the selection
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2));
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
- }
++ });
}
}
@@@ -105,15 -117,20 +109,17 @@@
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 2, 2);
- // order by a column not in the selection
- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
-
- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
-
- assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+ // order by a column not in the selection
+ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
-
++ row(0), row(1), row(2));
++
+ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
-
++ row(2), row(1), row(0));
++
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
- }
++ });
dropTable("DROP TABLE %s");
}
}
@@@ -129,62 -146,68 +135,64 @@@
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 4);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 2, 5);
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0, 0, 0, 0),
- row(0, 0, 1, 1),
- row(0, 0, 2, 2),
- row(0, 1, 0, 3),
- row(0, 1, 1, 4),
- row(0, 1, 2, 5)
- );
-
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(0, 1, 2, 5),
- row(0, 1, 1, 4),
- row(0, 1, 0, 3),
- row(0, 0, 2, 2),
- row(0, 0, 1, 1),
- row(0, 0, 0, 0)
- );
-
- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(0, 1, 2, 5),
- row(0, 1, 1, 4),
- row(0, 1, 0, 3),
- row(0, 0, 2, 2),
- row(0, 0, 1, 1),
- row(0, 0, 0, 0)
- );
-
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY d ASC", 0);
-
- // select and order by b
- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(0), row(0), row(1), row(1), row(1));
- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(1), row(1), row(1), row(0), row(0), row(0));
-
- // select c, order by b
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2), row(0), row(1), row(2));
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0), row(2), row(1), row(0));
-
- // select c, order by b, c
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
- row(0), row(1), row(2), row(0), row(1), row(2));
- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(2), row(1), row(0), row(2), row(1), row(0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- // select d, order by b, c
- assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
- row(0), row(1), row(2), row(3), row(4), row(5));
- assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(5), row(4), row(3), row(2), row(1), row(0));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0, 0, 0, 0),
+ row(0, 0, 1, 1),
+ row(0, 0, 2, 2),
+ row(0, 1, 0, 3),
+ row(0, 1, 1, 4),
+ row(0, 1, 2, 5)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(0, 1, 2, 5),
+ row(0, 1, 1, 4),
+ row(0, 1, 0, 3),
+ row(0, 0, 2, 2),
+ row(0, 0, 1, 1),
+ row(0, 0, 0, 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(0, 1, 2, 5),
+ row(0, 1, 1, 4),
+ row(0, 1, 0, 3),
+ row(0, 0, 2, 2),
+ row(0, 0, 1, 1),
+ row(0, 0, 0, 0)
+ );
+
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
+ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY d ASC", 0);
+
+ // select and order by b
+ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(0), row(0), row(1), row(1), row(1));
+ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(1), row(1), row(1), row(0), row(0), row(0));
+
+ // select c, order by b
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2), row(0), row(1), row(2));
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0), row(2), row(1), row(0));
+
+ // select c, order by b, c
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
+ row(0), row(1), row(2), row(0), row(1), row(2));
+ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(2), row(1), row(0), row(2), row(1), row(0));
+
+ // select d, order by b, c
+ assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
+ row(0), row(1), row(2), row(3), row(4), row(5));
+ assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(5), row(4), row(3), row(2), row(1), row(0));
- }
++ });
}
@Test
@@@ -198,41 -221,46 +206,42 @@@
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 4);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 2, 5);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c ASC", 0);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c DESC", 0);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY d ASC", 0);
-
- // select and order by b
- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(0), row(0), row(1), row(1), row(1));
- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(1), row(1), row(1), row(0), row(0), row(0));
-
- assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0, 0), row(0, 0), row(0, 0), row(1, 1), row(1, 1), row(1, 1));
- assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(1, 1), row(1, 1), row(1, 1), row(0, 0), row(0, 0), row(0, 0));
-
- // select c, order by b
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2), row(0), row(1), row(2));
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0), row(2), row(1), row(0));
-
- // select c, order by b, c
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
- row(0), row(1), row(2), row(0), row(1), row(2));
- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(2), row(1), row(0), row(2), row(1), row(0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- // select d, order by b, c
- assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
- row(0), row(1), row(2), row(3), row(4), row(5));
- assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
- row(5), row(4), row(3), row(2), row(1), row(0));
++ beforeAndAfterFlush(() -> {
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c ASC", 0);
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c DESC", 0);
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
+ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY d ASC", 0);
+ // select and order by b
+ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(0), row(0), row(1), row(1), row(1));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(1), row(1), row(1), row(0), row(0), row(0));
+
+ assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0, 0), row(0, 0), row(0, 0), row(1, 1), row(1, 1), row(1, 1));
+ assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(1, 1), row(1, 1), row(1, 1), row(0, 0), row(0, 0), row(0, 0));
+
+ // select c, order by b
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
+ row(0), row(1), row(2), row(0), row(1), row(2));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0), row(2), row(1), row(0));
+
+ // select c, order by b, c
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
+ row(0), row(1), row(2), row(0), row(1), row(2));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(2), row(1), row(0), row(2), row(1), row(0));
+
+ // select d, order by b, c
+ assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
+ row(0), row(1), row(2), row(3), row(4), row(5));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
+ row(5), row(4), row(3), row(2), row(1), row(0));
- }
++ });
}
/**
@@@ -247,8 -275,14 +256,10 @@@
for (int i = 0; i < 10; i++)
execute("INSERT INTO %s (k, c, v) VALUES (0, ?, ?)", i, i);
- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC"),
- row(9), row(8), row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC"),
+ row(9), row(8), row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
- }
++ });
createTable("CREATE TABLE %s (k int, c1 int, c2 int, v int, PRIMARY KEY (k, c1, c2)) WITH COMPACT STORAGE");
@@@ -256,15 -290,21 +267,17 @@@
for (int j = 0; j < 2; j++)
execute("INSERT INTO %s (k, c1, c2, v) VALUES (0, ?, ?, ?)", i, j, i * 2 + j);
- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC");
- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c2 DESC");
- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY k DESC");
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC");
+ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c2 DESC");
+ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY k DESC");
- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1 DESC"),
- row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
+ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1 DESC"),
+ row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1"),
- row(0), row(1), row(2), row(3), row(4), row(5), row(6), row(7));
+ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1"),
+ row(0), row(1), row(2), row(3), row(4), row(5), row(6), row(7));
- }
++ });
}
/**
@@@ -281,23 -321,29 +294,25 @@@
execute("INSERT INTO %s (row, number, string) VALUES ('row', 3, 'three')");
execute("INSERT INTO %s (row, number, string) VALUES ('row', 4, 'four')");
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number ASC"),
- row(1), row(2));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number ASC"),
+ row(1), row(2));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number ASC"),
- row(3), row(4));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number ASC"),
+ row(3), row(4));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number DESC"),
- row(2), row(1));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number DESC"),
+ row(2), row(1));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number DESC"),
- row(4), row(3));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number DESC"),
+ row(4), row(3));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number > 3 ORDER BY number DESC"),
- row(4));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number > 3 ORDER BY number DESC"),
+ row(4));
- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number <= 3 ORDER BY number DESC"),
- row(3), row(2), row(1));
+ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number <= 3 ORDER BY number DESC"),
+ row(3), row(2), row(1));
- }
++ });
}
/**
@@@ -330,20 -376,20 +345,22 @@@
execute("INSERT INTO %s (my_id, col1, value) VALUES ( 'key3', 2, 'b')");
execute("INSERT INTO %s (my_id, col1, value) VALUES ( 'key4', 4, 'd')");
- assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row(1), row(2), row(3));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
+ row(1), row(2), row(3));
- assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1 LIMIT 2"),
- row(1), row(2));
++ assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1 LIMIT 2"),
++ row(1), row(2));
+
- assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1 LIMIT 10"),
- row(1), row(2), row(3));
++ assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1 LIMIT 10"),
++ row(1), row(2), row(3));
+
- assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row(1, "key1"), row(2, "key3"), row(3, "key2"));
+ assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
+ row(1, "key1"), row(2, "key3"), row(3, "key2"));
- assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row("key1", 1), row("key3", 2), row("key2", 3));
+ assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
+ row("key1", 1), row("key3", 2), row("key2", 3));
- }
++ });
createTable("CREATE TABLE %s (pk1 int, pk2 int, c int, v text, PRIMARY KEY ((pk1, pk2), c) )");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 2, "A");
@@@ -351,34 -397,31 +368,36 @@@
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 4, "D");
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
- assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
- }
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
+ row("B", null, 1),
+ row("A", null, 2),
+ row("D", null, 4));
+
+ assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
+ row("B", null, 1),
+ row("A", null, 2),
+ row("D", null, 4));
+
+ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
+ row("B"),
+ row("A"),
+ row("D"));
+
+ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 2; ", 1, 1, 2),
+ row("B"),
+ row("A"));
+
+ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 10; ", 1, 1, 2),
+ row("B"),
+ row("A"),
+ row("D"));
+
+ assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
+ row("B"),
+ row("A"),
+ row("D"));
++ });
createTable("CREATE TABLE %s (pk1 int, pk2 int, c1 int, c2 int, v text, PRIMARY KEY ((pk1, pk2), c1, c2) )");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 4, "A");
@@@ -386,51 -429,31 +405,53 @@@
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 3, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 1, "D");
- assertRows(execute("SELECT v, ttl(v), c1, c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
- row("B", null, 1, 2),
- row("D", null, 4, 1),
- row("A", null, 4, 4));
-
- assertRows(execute("SELECT v, ttl(v), c1 as name_1, c2 as name_2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
- row("B", null, 1, 2),
- row("D", null, 4, 1),
- row("A", null, 4, 4));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
- row("B"),
- row("D"),
- row("A"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
- row("B"),
- row("D"),
- row("A"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2 LIMIT 2; ", 1, 1, 2),
- row("B"),
- row("D"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2 LIMIT 10; ", 1, 1, 2),
- row("B"),
- row("D"),
- row("A"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC; ", 1, 1, 2),
- row("A"),
- row("D"),
- row("B"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 2; ", 1, 1, 2),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 10; ", 1, 1, 2),
- row("A"),
- row("D"),
- row("B"));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- assertInvalidMessage("LIMIT must be strictly positive",
- "SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 0; ", 1, 1, 2);
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT v, ttl(v), c1, c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
+ row("B", null, 1, 2),
+ row("D", null, 4, 1),
+ row("A", null, 4, 4));
+
+ assertRows(execute("SELECT v, ttl(v), c1 as name_1, c2 as name_2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
+ row("B", null, 1, 2),
+ row("D", null, 4, 1),
+ row("A", null, 4, 4));
+
+ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
+ row("B"),
+ row("D"),
+ row("A"));
+
+ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
+ row("B"),
+ row("D"),
+ row("A"));
- }
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2 LIMIT 2; ", 1, 1, 2),
++ row("B"),
++ row("D"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2 LIMIT 10; ", 1, 1, 2),
++ row("B"),
++ row("D"),
++ row("A"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC; ", 1, 1, 2),
++ row("A"),
++ row("D"),
++ row("B"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 2; ", 1, 1, 2),
++ row("A"),
++ row("D"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 10; ", 1, 1, 2),
++ row("A"),
++ row("D"),
++ row("B"));
++
++ assertInvalidMessage("LIMIT must be strictly positive",
++ "SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1 DESC , c2 DESC LIMIT 0; ", 1, 1, 2);
++ });
}
@Test
@@@ -447,33 -470,39 +468,35 @@@
execute("UPDATE %s SET s = 2 WHERE a = 2");
execute("UPDATE %s SET s = 3 WHERE a = 3");
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC"),
- row(2, 2, 2, 1, 2),
- row(2, 2, 1, 1, 2),
- row(1, 1, 2, 1, 1),
- row(1, 1, 1, 1, 1),
- row(3, null, null, null, 3));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC"),
- row(3, null, null, null, 3),
- row(1, 1, 1, 1, 1),
- row(1, 1, 2, 1, 1),
- row(2, 2, 1, 1, 2),
- row(2, 2, 2, 1, 2));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC , c DESC"),
- row(2, 2, 2, 1, 2),
- row(2, 2, 1, 1, 2),
- row(1, 1, 2, 1, 1),
- row(1, 1, 1, 1, 1),
- row(3, null, null, null, 3));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC, c ASC"),
- row(3, null, null, null, 3),
- row(1, 1, 1, 1, 1),
- row(1, 1, 2, 1, 1),
- row(2, 2, 1, 1, 2),
- row(2, 2, 2, 1, 2));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC"),
+ row(2, 2, 2, 1, 2),
+ row(2, 2, 1, 1, 2),
+ row(1, 1, 2, 1, 1),
+ row(1, 1, 1, 1, 1),
+ row(3, null, null, null, 3));
+
+ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC"),
+ row(3, null, null, null, 3),
+ row(1, 1, 1, 1, 1),
+ row(1, 1, 2, 1, 1),
+ row(2, 2, 1, 1, 2),
+ row(2, 2, 2, 1, 2));
+
+ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC , c DESC"),
+ row(2, 2, 2, 1, 2),
+ row(2, 2, 1, 1, 2),
+ row(1, 1, 2, 1, 1),
+ row(1, 1, 1, 1, 1),
+ row(3, null, null, null, 3));
+
+ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC, c ASC"),
+ row(3, null, null, null, 3),
+ row(1, 1, 1, 1, 1),
+ row(1, 1, 2, 1, 1),
+ row(2, 2, 1, 1, 2),
+ row(2, 2, 2, 1, 2));
- }
++ });
}
/**
@@@ -488,13 -517,19 +511,15 @@@
for(int i =0; i < 10; i++)
execute("INSERT INTO %s (k, c, v) VALUES (0, ?, ?)", i, i);
- assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c ASC"),
- row(0, 0), row(1, 1), row(2, 2), row(3, 3), row(4, 4),
- row(5, 5), row(6, 6), row(7, 7), row(8, 8), row(9, 9));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c ASC"),
+ row(0, 0), row(1, 1), row(2, 2), row(3, 3), row(4, 4),
+ row(5, 5), row(6, 6), row(7, 7), row(8, 8), row(9, 9));
- assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c DESC"),
- row(9, 9), row(8, 8), row(7, 7), row(6, 6), row(5, 5),
- row(4, 4), row(3, 3), row(2, 2), row(1, 1), row(0, 0));
+ assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c DESC"),
+ row(9, 9), row(8, 8), row(7, 7), row(6, 6), row(5, 5),
+ row(4, 4), row(3, 3), row(2, 2), row(1, 1), row(0, 0));
- }
++ });
createTable("CREATE TABLE %s (k int, c1 int, c2 int, v text, PRIMARY KEY (k, c1, c2)) WITH CLUSTERING ORDER BY (c1 ASC, c2 DESC)");
@@@ -502,28 -537,34 +527,30 @@@
for(int j = 0; j < 10; j++)
execute("INSERT INTO %s (k, c1, c2, v) VALUES (0, ?, ?, ?)", i, j, String.format("%d%d", i, j));
- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 ASC");
- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 DESC");
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 ASC");
+ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 DESC");
- Object[][] expectedRows = new Object[100][];
- for(int i = 0; i < 10; i++)
- for(int j = 9; j >= 0; j--)
- expectedRows[i * 10 + (9 - j)] = row(i, j, String.format("%d%d", i, j));
+ Object[][] expectedRows = new Object[100][];
+ for(int i = 0; i < 10; i++)
+ for(int j = 9; j >= 0; j--)
+ expectedRows[i * 10 + (9 - j)] = row(i, j, String.format("%d%d", i, j));
- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC"),
- expectedRows);
+ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC"),
+ expectedRows);
- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC"),
- expectedRows);
+ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC"),
+ expectedRows);
- for(int i = 9; i >= 0; i--)
- for(int j = 0; j < 10; j++)
- expectedRows[(9 - i) * 10 + j] = row(i, j, String.format("%d%d", i, j));
+ for(int i = 9; i >= 0; i--)
+ for(int j = 0; j < 10; j++)
+ expectedRows[(9 - i) * 10 + j] = row(i, j, String.format("%d%d", i, j));
- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC"),
- expectedRows);
+ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC"),
+ expectedRows);
- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c2 DESC, c1 ASC");
+ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c2 DESC, c1 ASC");
- }
++ });
}
/**
@@@ -534,22 -575,28 +561,24 @@@
{
createTable("CREATE TABLE %s (k text, c1 int, c2 int, PRIMARY KEY (k, c1, c2) ) WITH CLUSTERING ORDER BY (c1 ASC, c2 DESC)");
-- for (int i = 0; i < 2; i++)
-- for (int j = 0; j < 2; j++)
-- execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
-
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
++ for (int i = 0; i < 2; i++)
++ for (int j = 0; j < 2; j++)
++ execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
+ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
+ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
- row(1, 0), row(1, 1), row(0, 0), row(0, 1));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
+ row(1, 0), row(1, 1), row(0, 0), row(0, 1));
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
- }
++ });
}
/**
@@@ -563,9 -610,15 +592,11 @@@
execute("INSERT INTO %s(k, c1, c2) VALUES (0, 0, 1)");
execute("INSERT INTO %s(k, c1, c2) VALUES (0, 0, 2)");
- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
- row(0, 0, 2),
- row(0, 0, 0));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
+ row(0, 0, 2),
+ row(0, 0, 0));
- }
++ });
}
/**
@@@ -584,45 -637,51 +615,47 @@@
execute("INSERT INTO %s (k, c1, c2, v) VALUES (1, 1, 1, 4)");
execute("INSERT INTO %s (k, c1, c2, v) VALUES (1, 1, 2, 5)");
- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
- row(0, 0, 0, 0),
- row(0, 0, 2, 2));
- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC, c2 ASC"),
- row(0, 0, 0, 0),
- row(0, 0, 2, 2));
-
- // check that we don 't need to select the column on which we order
- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
- row(0),
- row(2));
- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC"),
- row(0),
- row(2));
- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
- row(2),
- row(0));
-
- assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0)"),
- row(0),
- row(1),
- row(2),
- row(3),
- row(4),
- row(5));
-
- assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"),
- row(0),
- row(1),
- row(2),
- row(3),
- row(4),
- row(5));
-
- // we should also be able to use functions in the select clause (additional test for CASSANDRA - 8286)
- Object[][] results = getRows(execute("SELECT writetime(v) FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- // since we don 't know the write times, just assert that the order matches the order we expect
- assertTrue(isFirstIntSorted(results));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
+ row(0, 0, 0, 0),
+ row(0, 0, 2, 2));
+ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC, c2 ASC"),
+ row(0, 0, 0, 0),
+ row(0, 0, 2, 2));
+
+ // check that we don 't need to select the column on which we order
+ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
+ row(0),
+ row(2));
+ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC"),
+ row(0),
+ row(2));
+ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
+ row(2),
+ row(0));
+
+ assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0)"),
+ row(0),
+ row(1),
+ row(2),
+ row(3),
+ row(4),
+ row(5));
+
+ assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"),
+ row(0),
+ row(1),
+ row(2),
+ row(3),
+ row(4),
+ row(5));
+
+ // we should also be able to use functions in the select clause (additional test for CASSANDRA - 8286)
+ Object[][] results = getRows(execute("SELECT writetime(v) FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"));
+
+ // since we don 't know the write times, just assert that the order matches the order we expect
+ assertTrue(isFirstIntSorted(results));
- }
++ });
}
@Test
@@@ -637,33 -696,39 +670,35 @@@
execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 2, 10);
execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 2, 11);
- assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3;", 1, 1, 2),
- row(1, 1, 1),
- row(1, 1, 2),
- row(1, 2, 10),
- row(1, 2, 11),
- row(1, 1, 13));
-
- assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3 desc;", 1, 1, 2),
- row(1, 1, 13),
- row(1, 2, 11),
- row(1, 2, 10),
- row(1, 1, 2),
- row(1, 1, 1));
-
- assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3;", 1, 2, 1),
- row(1, 1, 1),
- row(1, 1, 2),
- row(1, 2, 10),
- row(1, 2, 11),
- row(1, 1, 13));
- for (boolean flush : new boolean[]{true, false})
- {
- if (flush)
- flush();
--
- assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3 desc;", 1, 2, 1),
- row(1, 1, 13),
- row(1, 2, 11),
- row(1, 2, 10),
- row(1, 1, 2),
- row(1, 1, 1));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3;", 1, 1, 2),
+ row(1, 1, 1),
+ row(1, 1, 2),
+ row(1, 2, 10),
+ row(1, 2, 11),
+ row(1, 1, 13));
+
+ assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3 desc;", 1, 1, 2),
+ row(1, 1, 13),
+ row(1, 2, 11),
+ row(1, 2, 10),
+ row(1, 1, 2),
+ row(1, 1, 1));
+
+ assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3;", 1, 2, 1),
+ row(1, 1, 1),
+ row(1, 1, 2),
+ row(1, 2, 10),
+ row(1, 2, 11),
+ row(1, 1, 13));
+
+ assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3 desc;", 1, 2, 1),
+ row(1, 1, 13),
+ row(1, 2, 11),
+ row(1, 2, 10),
+ row(1, 1, 2),
+ row(1, 1, 1));
- }
++ });
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index 1b6fe9b,ac1ba4c..a5f153e
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@@ -22,16 -22,19 +22,18 @@@ import java.util.UUID
import org.junit.Test;
--import junit.framework.Assert;
++import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.exceptions.InvalidRequestException;
- import org.apache.cassandra.utils.ByteBufferUtil;
- import org.apache.cassandra.cql3.CQLTester;
-import org.apache.cassandra.cql3.CQLTester;
-
++import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
++import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
-import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
++import junit.framework.Assert;
+
/**
* Test column ranges and ordering with static column in table
*/
@@@ -2576,4 -2324,385 +2578,349 @@@ public class SelectTest extends CQLTest
assertInvalidMessage("PRIMARY KEY column \"c2\" cannot be restricted as preceding column \"c1\" is not restricted",
"SELECT * FROM %s WHERE pk = 1 AND c2 = 1 ALLOW FILTERING;");
}
+
+ @Test
- public void testIndexQueryWithCompositePartitionKey() throws Throwable
- {
- createTable("CREATE TABLE %s (p1 int, p2 int, v int, PRIMARY KEY ((p1, p2)))");
- assertInvalidMessage("Partition key parts: p2 must be restricted as other parts are",
- "SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING");
-
- createIndex("CREATE INDEX ON %s(v)");
-
- execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 1, 3);
- execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 2, 3);
- execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 2, 1, 3);
-
- assertRows(execute("SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING"),
- row(1, 2, 3),
- row(1, 1, 3));
- }
-
- @Test
+ public void testEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("2"), bytes("2"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk = textAsBlob('');");
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertInvalidMessage("Key may not be empty",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ EMPTY_BYTE_BUFFER, bytes("2"), bytes("2"));
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('');"));
- }
++ });
+
+ if (options.contains("COMPACT"))
+ {
- assertInvalidMessage("Missing PRIMARY KEY part c",
++ assertInvalidMessage("Invalid empty or null value for column c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('');"));
- }
++ });
+ }
+
+ // Test restrictions on non-primary key value
- assertInvalidMessage("Predicates on non-primary-key columns (v) are not yet supported for non secondary index queries",
- "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
++ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;"));
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
++ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
++
++ beforeAndAfterFlush(() -> {
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;"),
++ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
++ });
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumns() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob('1'), textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob('1'), textAsBlob(''));"));
- }
++ });
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) < (textAsBlob(''), textAsBlob('1'));"));
- }
++ });
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("1"),
+ bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("2"),
+ bytes("2"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause));
- }
++
++ });
+
+ if (options.contains("COMPACT"))
+ {
- assertInvalidMessage("Missing PRIMARY KEY part c",
++ assertInvalidMessage("Invalid empty or null value for column c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ EMPTY_BYTE_BUFFER,
+ bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
- }
++ });
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumnsAndOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c1 DESC, c2 DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c1 DESC, c2 DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c1 DESC, c2 DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
- }
++ });
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
- }
++ });
+ }
+ }
}
[11/12] cassandra git commit: Merge branch cassandra-3.0 into
cassandra-3.9
Posted by bl...@apache.org.
Merge branch cassandra-3.0 into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/cb97649f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/cb97649f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/cb97649f
Branch: refs/heads/trunk
Commit: cb97649f51bb01285e813daae65cc0287e33b6c2
Parents: de2c449 bb56193
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 16 15:39:11 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 16 15:41:34 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 9 +
.../apache/cassandra/db/BufferClustering.java | 4 -
.../columniterator/AbstractSSTableIterator.java | 24 +-
.../db/columniterator/SSTableIterator.java | 17 +
.../columniterator/SSTableReversedIterator.java | 19 +-
.../cassandra/db/compaction/Scrubber.java | 169 +++-
.../cassandra/db/marshal/ReversedType.java | 10 -
.../db/partitions/AbstractBTreePartition.java | 7 +-
.../db/partitions/ImmutableBTreePartition.java | 34 +-
.../db/rows/UnfilteredRowIterators.java | 28 +
.../validation/entities/SecondaryIndexTest.java | 131 ++++
.../cql3/validation/operations/DeleteTest.java | 113 +++
.../operations/SelectOrderByTest.java | 765 ++++++++++---------
.../cql3/validation/operations/SelectTest.java | 355 ++++++++-
.../cassandra/db/marshal/ReversedTypeTest.java | 4 +-
16 files changed, 1268 insertions(+), 422 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/CHANGES.txt
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index 249077d,0bd3920..2edff8e
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -13,35 -13,18 +13,44 @@@ restore snapshots created with the prev
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
-3.0.9
-=====
++3.9
++===
+
+ Upgrading
+ ---------
+ - The ReversedType behaviour has been corrected for clustering columns of
+ BYTES type containing empty value. Scrub should be run on the existing
+ SSTables containing a descending clustering column of BYTES type to correct
+ their ordering. See CASSANDRA-12127 for more details.
-3.0.8
-=====
+3.8
+===
+
+New features
+------------
+ - Shared pool threads are now named according to the stage they are executing
+ tasks for. Thread names mentioned in traced queries change accordingly.
+ - A new option has been added to cassandra-stress "-rate fixed={number}/s"
+ that forces a scheduled rate of operations/sec over time. Using this, stress can
+ accurately account for coordinated ommission from the stress process.
+ - The cassandra-stress "-rate limit=" option has been renamed to "-rate throttle="
+ - hdr histograms have been added to stress runs, it's output can be saved to disk using:
+ "-log hdrfile=" option. This histogram includes response/service/wait times when used with the
+ fixed or throttle rate options. The histogram file can be plotted on
+ http://hdrhistogram.github.io/HdrHistogram/plotFiles.html
+ - TimeWindowCompactionStrategy has been added. This has proven to be a better approach
+ to time series compaction and new tables should use this instead of DTCS. See
+ CASSANDRA-9666 for details.
+ - Change-Data-Capture is now available. See cassandra.yaml and for cdc-specific flags and
+ a brief explanation of on-disk locations for archived data in CommitLog form. This can
+ be enabled via ALTER TABLE ... WITH cdc=true.
+ Upon flush, CommitLogSegments containing data for CDC-enabled tables are moved to
+ the data/cdc_raw directory until removed by the user and writes to CDC-enabled tables
+ will be rejected with a WriteTimeoutException once cdc_total_space_in_mb is reached
+ between unflushed CommitLogSegments and cdc_raw.
+ NOTE: CDC is disabled by default in the .yaml file. Do not enable CDC on a mixed-version
+ cluster as it will lead to exceptions which can interrupt traffic. Once all nodes
+ have been upgraded to 3.8 it is safe to enable this feature and restart the cluster.
Upgrading
---------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/BufferClustering.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/BufferClustering.java
index 7c6bb20,0000000..df6a473
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/BufferClustering.java
+++ b/src/java/org/apache/cassandra/db/BufferClustering.java
@@@ -1,44 -1,0 +1,40 @@@
+/*
+ * 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;
+
+import java.nio.ByteBuffer;
+
- import org.apache.cassandra.config.CFMetaData;
- import org.apache.cassandra.config.ColumnDefinition;
- import org.apache.cassandra.utils.memory.AbstractAllocator;
-
+/**
+ * The clustering column values for a row.
+ * <p>
+ * A {@code Clustering} is a {@code ClusteringPrefix} that must always be "complete", i.e. have
+ * as many values as there is clustering columns in the table it is part of. It is the clustering
+ * prefix used by rows.
+ * <p>
+ * Note however that while it's size must be equal to the table clustering size, a clustering can have
+ * {@code null} values, and this mostly for thrift backward compatibility (in practice, if a value is null,
+ * all of the following ones will be too because that's what thrift allows, but it's never assumed by the
+ * code so we could start generally allowing nulls for clustering columns if we wanted to).
+ */
+public class BufferClustering extends AbstractBufferClusteringPrefix implements Clustering
+{
+ BufferClustering(ByteBuffer... values)
+ {
+ super(Kind.CLUSTERING, values);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/columniterator/AbstractSSTableIterator.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/columniterator/AbstractSSTableIterator.java
index 005bb2c,d57e6bc..5959a20
--- a/src/java/org/apache/cassandra/db/columniterator/AbstractSSTableIterator.java
+++ b/src/java/org/apache/cassandra/db/columniterator/AbstractSSTableIterator.java
@@@ -47,13 -46,8 +47,12 @@@ public abstract class AbstractSSTableIt
private final boolean isForThrift;
+ protected final SegmentedFile ifile;
+
private boolean isClosed;
+ protected final Slices slices;
- protected int slice;
+
@SuppressWarnings("resource") // We need this because the analysis is not able to determine that we do close
// file on every path where we created it.
protected AbstractSSTableIterator(SSTableReader sstable,
@@@ -119,9 -109,6 +118,9 @@@
this.reader = needsReader ? createReader(indexEntry, file, shouldCloseFile) : null;
}
+ if (reader != null && !slices.isEmpty())
- reader.setForSlice(slices.get(0));
++ reader.setForSlice(nextSlice());
+
if (reader == null && file != null && shouldCloseFile)
file.close();
}
@@@ -145,6 -132,6 +144,23 @@@
}
}
++ private Slice nextSlice()
++ {
++ return slices.get(nextSliceIndex());
++ }
++
++ /**
++ * Returns the index of the next slice to process.
++ * @return the index of the next slice to process
++ */
++ protected abstract int nextSliceIndex();
++
++ /**
++ * Checks if there are more slice to process.
++ * @return {@code true} if there are more slice to process, {@code false} otherwise.
++ */
++ protected abstract boolean hasMoreSlices();
++
private static Row readStaticRow(SSTableReader sstable,
FileDataInput file,
SerializationHelper helper,
@@@ -233,19 -216,7 +249,19 @@@
public boolean hasNext()
{
- return reader != null && reader.hasNext();
+ while (true)
+ {
+ if (reader == null)
+ return false;
+
+ if (reader.hasNext())
+ return true;
+
- if (++slice >= slices.size())
++ if (!hasMoreSlices())
+ return false;
+
- slice(slices.get(slice));
++ slice(nextSlice());
+ }
}
public Unfiltered next()
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/columniterator/SSTableIterator.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/columniterator/SSTableIterator.java
index e4f6700,0409310..56ba8f4
--- a/src/java/org/apache/cassandra/db/columniterator/SSTableIterator.java
+++ b/src/java/org/apache/cassandra/db/columniterator/SSTableIterator.java
@@@ -32,6 -31,11 +32,11 @@@ import org.apache.cassandra.io.util.Seg
*/
public class SSTableIterator extends AbstractSSTableIterator
{
- public SSTableIterator(SSTableReader sstable, DecoratedKey key, ColumnFilter columns, boolean isForThrift)
- {
- this(sstable, null, key, sstable.getPosition(key, SSTableReader.Operator.EQ), columns, isForThrift);
- }
++ /**
++ * The index of the slice being processed.
++ */
++ private int slice;
+
public SSTableIterator(SSTableReader sstable,
FileDataInput file,
DecoratedKey key,
@@@ -51,6 -53,6 +56,18 @@@
: new ForwardReader(file, shouldCloseFile);
}
++ protected int nextSliceIndex()
++ {
++ int next = slice;
++ slice++;
++ return next;
++ }
++
++ protected boolean hasMoreSlices()
++ {
++ return slice < slices.size();
++ }
++
public boolean isReverseOrder()
{
return false;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
index 903fd59,3e49a3a..b8b6f76
--- a/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
+++ b/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
@@@ -35,6 -34,11 +35,11 @@@ import org.apache.cassandra.utils.btree
*/
public class SSTableReversedIterator extends AbstractSSTableIterator
{
- public SSTableReversedIterator(SSTableReader sstable, DecoratedKey key, ColumnFilter columns, boolean isForThrift)
- {
- this(sstable, null, key, sstable.getPosition(key, SSTableReader.Operator.EQ), columns, isForThrift);
- }
++ /**
++ * The index of the slice being processed.
++ */
++ private int slice;
+
public SSTableReversedIterator(SSTableReader sstable,
FileDataInput file,
DecoratedKey key,
@@@ -59,6 -61,6 +64,18 @@@
return true;
}
++ protected int nextSliceIndex()
++ {
++ int next = slice;
++ slice++;
++ return slices.size() - (next + 1);
++ }
++
++ protected boolean hasMoreSlices()
++ {
++ return slice < slices.size();
++ }
++
private class ReverseReader extends Reader
{
protected ReusablePartitionData buffer;
@@@ -106,7 -108,7 +123,7 @@@
buffer = createBuffer(1);
// Note that we can reuse that buffer between slices (we could alternatively re-read from disk
// every time, but that feels more wasteful) so we want to include everything from the beginning.
-- // We can stop at the slice end however since any following slice will be before that.
++ // We can stop at the last slice end however since any following slice will be before that.
loadFromDisk(null, slice.end(), true);
}
setIterator(slice);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
index 7fb01a3,c63acc2..4a8e4f2
--- a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
@@@ -270,8 -282,47 +270,13 @@@ public abstract class AbstractBTreePart
}
}
- public class SliceableIterator extends AbstractIterator implements SliceableUnfilteredRowIterator
- {
- private Iterator<Unfiltered> iterator;
-
- protected SliceableIterator(ColumnFilter selection, boolean isReversed)
- {
- super(selection, isReversed);
- }
-
- protected Unfiltered computeNext()
- {
- if (iterator == null)
- iterator = unfilteredIterator(selection, Slices.ALL, isReverseOrder);
- if (!iterator.hasNext())
- return endOfData();
- return iterator.next();
- }
-
- public Iterator<Unfiltered> slice(Slice slice)
- {
- return sliceIterator(selection, slice, isReverseOrder, current, staticRow);
- }
- }
-
- public SliceableUnfilteredRowIterator sliceableUnfilteredIterator(ColumnFilter columns, boolean reversed)
- {
- return new SliceableIterator(columns, reversed);
- }
-
- protected SliceableUnfilteredRowIterator sliceableUnfilteredIterator()
- {
- return sliceableUnfilteredIterator(ColumnFilter.all(metadata), false);
- }
-
protected static Holder build(UnfilteredRowIterator iterator, int initialRowCapacity)
{
+ return build(iterator, initialRowCapacity, true);
+ }
+
+ protected static Holder build(UnfilteredRowIterator iterator, int initialRowCapacity, boolean ordered)
+ {
CFMetaData metadata = iterator.metadata();
PartitionColumns columns = iterator.columns();
boolean reversed = iterator.isReverseOrder();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
index d324190,43653a9..46447ec
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
@@@ -230,6 -213,60 +230,34 @@@ public abstract class UnfilteredRowIter
}
/**
+ * Returns an iterator that concatenate the specified atom with the iterator.
+ */
+ public static UnfilteredRowIterator concat(final Unfiltered first, final UnfilteredRowIterator rest)
+ {
+ return new WrappingUnfilteredRowIterator(rest)
+ {
+ private boolean hasReturnedFirst;
+
+ @Override
+ public boolean hasNext()
+ {
+ return hasReturnedFirst ? super.hasNext() : true;
+ }
+
+ @Override
+ public Unfiltered next()
+ {
+ if (!hasReturnedFirst)
+ {
+ hasReturnedFirst = true;
+ return first;
+ }
+ return super.next();
+ }
+ };
+ }
+
- public static UnfilteredRowIterator cloningIterator(UnfilteredRowIterator iterator, final AbstractAllocator allocator)
- {
- class Cloner extends Transformation
- {
- private final Row.Builder builder = allocator.cloningBTreeRowBuilder();
-
- public Row applyToStatic(Row row)
- {
- return Rows.copy(row, builder).build();
- }
-
- @Override
- public Row applyToRow(Row row)
- {
- return Rows.copy(row, builder).build();
- }
-
- @Override
- public RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
- {
- return marker.copy(allocator);
- }
- }
- return Transformation.apply(iterator, new Cloner());
- }
-
+ /**
* Validate that the data of the provided iterator is valid, that is that the values
* it contains are valid for the type they represent, and more generally that the
* infos stored are sensible.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
index 9b92ebb,b9ccafb..118a1e2
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
@@@ -1107,28 -1058,142 +1112,136 @@@ public class DeleteTest extends CQLTest
}
@Test
+ public void testDeleteAndReverseQueries() throws Throwable
+ {
+ // This test insert rows in one sstable and a range tombstone covering some of those rows in another, and it
+ // validates we correctly get only the non-removed rows when doing reverse queries.
+
+ createTable("CREATE TABLE %s (k text, i int, PRIMARY KEY (k, i))");
+
+ for (int i = 0; i < 10; i++)
+ execute("INSERT INTO %s(k, i) values (?, ?)", "a", i);
+
+ flush();
+
+ execute("DELETE FROM %s WHERE k = ? AND i >= ? AND i <= ?", "a", 2, 7);
+
+ assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
+ row(9), row(8), row(1), row(0)
+ );
+
+ flush();
+
+ assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
+ row(9), row(8), row(1), row(0)
+ );
+ }
++
++ @Test
+ public void testDeleteWithEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY (pk, c))" + options);
+
+ if (StringUtils.isEmpty(options))
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')");
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+ }
+ else
+ {
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('')");
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))");
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')");
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')");
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+ }
+ }
+
+ @Test
+ public void testDeleteWithMultipleClusteringColumnsAndEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("0"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')");
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("0")));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 >= textAsBlob('')");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')");
+
+ assertEmpty(execute("SELECT * FROM %s"));
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
+
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 <= textAsBlob('')");
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 < textAsBlob('')");
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("3")));
+ }
+ }
-
- private void flush(boolean forceFlush)
- {
- if (forceFlush)
- flush();
- }
-
- @Test
- public void testDeleteAndReverseQueries() throws Throwable
- {
- // This test insert rows in one sstable and a range tombstone covering some of those rows in another, and it
- // validates we correctly get only the non-removed rows when doing reverse queries.
-
- createTable("CREATE TABLE %s (k text, i int, PRIMARY KEY (k, i))");
-
- for (int i = 0; i < 10; i++)
- execute("INSERT INTO %s(k, i) values (?, ?)", "a", i);
-
- flush();
-
- execute("DELETE FROM %s WHERE k = ? AND i >= ? AND i <= ?", "a", 2, 7);
-
- assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
- row(9), row(8), row(1), row(0)
- );
-
- flush();
-
- assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
- row(9), row(8), row(1), row(0)
- );
- }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
index 6e06419,bb35458..bfc0c86
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
@@@ -105,15 -109,17 +109,17 @@@ public class SelectOrderByTest extends
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 2, 2);
- // order by a column not in the selection
- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
+ beforeAndAfterFlush(() -> {
+ // order by a column not in the selection
+ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
- row(0), row(1), row(2));
-
++ row(0), row(1), row(2));
+
- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
+ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
-
++ row(2), row(1), row(0));
+
- assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
- row(2), row(1), row(0));
+ assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
+ row(2), row(1), row(0));
+ });
dropTable("DROP TABLE %s");
}
}
@@@ -351,34 -368,36 +368,36 @@@
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 4, "D");
- assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 2; ", 1, 1, 2),
- row("B"),
- row("A"));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 10; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
+ beforeAndAfterFlush(() -> {
- assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B", null, 1),
- row("A", null, 2),
- row("D", null, 4));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 2; ", 1, 1, 2),
- row("B"),
- row("A"));
-
- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 10; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
-
- assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
- row("B"),
- row("A"),
- row("D"));
++ assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B", null, 1),
++ row("A", null, 2),
++ row("D", null, 4));
++
++ assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B", null, 1),
++ row("A", null, 2),
++ row("D", null, 4));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 2; ", 1, 1, 2),
++ row("B"),
++ row("A"));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c LIMIT 10; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
++
++ assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
+ });
createTable("CREATE TABLE %s (pk1 int, pk2 int, c1 int, c2 int, v text, PRIMARY KEY ((pk1, pk2), c1, c2) )");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 4, "A");
@@@ -534,22 -561,24 +561,24 @@@
{
createTable("CREATE TABLE %s (k text, c1 int, c2 int, PRIMARY KEY (k, c1, c2) ) WITH CLUSTERING ORDER BY (c1 ASC, c2 DESC)");
- beforeAndAfterFlush(() -> {
- for (int i = 0; i < 2; i++)
- for (int j = 0; j < 2; j++)
- execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
+ for (int i = 0; i < 2; i++)
+ for (int j = 0; j < 2; j++)
+ execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
++ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
+ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
+ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
- row(1, 0), row(1, 1), row(0, 0), row(0, 1));
+ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
+ row(1, 0), row(1, 1), row(0, 0), row(0, 1));
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
+ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
+ });
}
/**
http://git-wip-us.apache.org/repos/asf/cassandra/blob/cb97649f/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index 9a1493b,a5f153e..db0a4cd
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@@ -21,16 -21,18 +21,18 @@@ import java.nio.ByteBuffer
import java.util.UUID;
import org.junit.Test;
++import org.junit.Assert;
- import junit.framework.Assert;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.exceptions.InvalidRequestException;
- import org.apache.cassandra.utils.ByteBufferUtil;
-import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
-import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
++import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
++import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+
-import junit.framework.Assert;
/**
* Test column ranges and ordering with static column in table
@@@ -2973,22 -2571,356 +2975,367 @@@ public class SelectTest extends CQLTest
assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 IN(0,1,2) AND c2 = 1 AND v = 3"),
row(1, 1, 1, 3, 3));
+ }
- assertInvalidMessage("Clustering column \"c2\" cannot be restricted (preceding column \"c1\" is restricted by a non-EQ relation)",
- "SELECT * FROM %s WHERE pk = 1 AND c1 > 0 AND c1 < 5 AND c2 = 1 ALLOW FILTERING;");
+ @Test
+ public void testIndexQueryWithCompositePartitionKey() throws Throwable
+ {
+ createTable("CREATE TABLE %s (p1 int, p2 int, v int, PRIMARY KEY ((p1, p2)))");
+ assertInvalidMessage("Partition key parts: p2 must be restricted as other parts are",
+ "SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING");
+ createIndex("CREATE INDEX ON %s(v)");
- assertInvalidMessage("PRIMARY KEY column \"c2\" cannot be restricted as preceding column \"c1\" is not restricted",
- "SELECT * FROM %s WHERE pk = 1 AND c2 = 1 ALLOW FILTERING;");
+ execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 1, 3);
+ execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 2, 3);
+ execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 2, 1, 3);
+
+ assertRows(execute("SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING"),
+ row(1, 2, 3),
+ row(1, 1, 3));
}
+
+ @Test
+ public void testEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("2"), bytes("2"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk = textAsBlob('');");
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertInvalidMessage("Key may not be empty",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ EMPTY_BYTE_BUFFER, bytes("2"), bytes("2"));
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('');"));
+ });
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Invalid empty or null value for column c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('');"));
+ });
+ }
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;"));
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
+
+ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
+ });
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumns() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob('1'), textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob('1'), textAsBlob(''));"));
+ });
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ beforeAndAfterFlush(() -> {
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) < (textAsBlob(''), textAsBlob('1'));"));
+ });
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("1"),
+ bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("2"),
+ bytes("2"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause));
+
+ });
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Invalid empty or null value for column c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ EMPTY_BYTE_BUFFER,
+ bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+ });
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumnsAndOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c1 DESC, c2 DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c1 DESC, c2 DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c1 DESC, c2 DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+ });
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+ });
+ }
+ }
}
[07/12] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by bl...@apache.org.
Merge branch cassandra-2.1 into cassandra-2.2
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/9583b6b3
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/9583b6b3
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/9583b6b3
Branch: refs/heads/trunk
Commit: 9583b6b312866a4861df1a795329695b3fc09328
Parents: 88b3cfc 527d189
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 16 15:25:42 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 16 15:26:26 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 10 +
.../AbstractPrimaryKeyRestrictions.java | 5 +-
.../cql3/restrictions/AbstractRestriction.java | 10 +-
.../ForwardingPrimaryKeyRestrictions.java | 31 +-
.../restrictions/MultiColumnRestriction.java | 48 +-
.../restrictions/PrimaryKeyRestrictionSet.java | 36 +-
.../restrictions/PrimaryKeyRestrictions.java | 9 +-
.../cql3/restrictions/Restriction.java | 16 +-
.../ReversedPrimaryKeyRestrictions.java | 9 +-
.../restrictions/SingleColumnRestriction.java | 57 +-
.../restrictions/StatementRestrictions.java | 23 +-
.../cql3/restrictions/TokenFilter.java | 36 +-
.../cql3/restrictions/TokenRestriction.java | 13 +-
.../cql3/statements/ModificationStatement.java | 4 +-
.../cql3/statements/SelectStatement.java | 11 +-
.../cql3/statements/UpdateStatement.java | 2 +-
.../cassandra/db/compaction/Scrubber.java | 159 +++-
.../cassandra/db/marshal/ReversedType.java | 10 -
.../PrimaryKeyRestrictionSetTest.java | 372 ++++-----
.../validation/entities/SecondaryIndexTest.java | 142 ++++
.../cql3/validation/operations/DeleteTest.java | 42 +
.../operations/SelectOrderByTest.java | 758 +++++++++++--------
.../cql3/validation/operations/SelectTest.java | 372 ++++++++-
.../cassandra/db/marshal/ReversedTypeTest.java | 4 +-
25 files changed, 1540 insertions(+), 640 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 394598a,dee669a..c421398
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,24 -1,5 +1,25 @@@
-2.1.16
- * Fix queries with empty ByteBuffer values in clustering column restrictions (CASSANDRA-12127)
+2.2.8
+ * Only set broadcast_rpc_address on Ec2MultiRegionSnitch if it's not set (CASSANDRA-11357)
+ * Update StorageProxy range metrics for timeouts, failures and unavailables (CASSANDRA-9507)
+ * Add Sigar to classes included in clientutil.jar (CASSANDRA-11635)
+ * Add decay to histograms and timers used for metrics (CASSANDRA-11752)
+ * Fix hanging stream session (CASSANDRA-10992)
+ * Add byteman support for testing (CASSANDRA-12377)
+ * Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
+ * Restore JVM metric export for metric reporters (CASSANDRA-12312)
+ * Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
+ * Revert CASSANDRA-11427 (CASSANDRA-12351)
+ * Wait for tracing events before returning response and query at same consistency level client side (CASSANDRA-11465)
+ * cqlsh copyutil should get host metadata by connected address (CASSANDRA-11979)
+ * Fixed cqlshlib.test.remove_test_db (CASSANDRA-12214)
+ * Synchronize ThriftServer::stop() (CASSANDRA-12105)
+ * Use dedicated thread for JMX notifications (CASSANDRA-12146)
+ * NPE when trying to remove purgable tombstones from result (CASSANDRA-12143)
+ * Improve streaming synchronization and fault tolerance (CASSANDRA-11414)
+ * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
+ * Don't write shadowed range tombstone (CASSANDRA-12030)
+Merged from 2.1:
++ * Fix queries with empty ByteBuffer values in clustering column restrictions (CASSANDRA-12127)
* Disable passing control to post-flush after flush failure to prevent data loss (CASSANDRA-11828)
* Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
* cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index f0712eb,6a70adc..0a3ab36
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -13,13 -13,18 +13,23 @@@ restore snapshots created with the prev
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
-2.1.16
-======
++2.2.8
++=====
+
+ Upgrading
+ ---------
+ - The ReversedType behaviour has been corrected for clustering columns of
+ BYTES type containing empty value. Scrub should be run on the existing
+ SSTables containing a descending clustering column of BYTES type to correct
- their ordering. See CASSANDRA-12127 for details.
++ their ordering. See CASSANDRA-12127 for more details.
+
-2.1.15
-======
+2.2.7
+=====
+
+New features
+------------
+ - JSON timestamps are now in UTC and contain the timezone information, see
+ CASSANDRA-11137 for more details.
Upgrading
---------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
index 2eaa386,0000000..51c3e26
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
@@@ -1,60 -1,0 +1,61 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.CType;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * Base class for <code>PrimaryKeyRestrictions</code>.
+ */
+abstract class AbstractPrimaryKeyRestrictions extends AbstractRestriction implements PrimaryKeyRestrictions
+{
+ /**
+ * The composite type.
+ */
+ protected final CType ctype;
+
+ public AbstractPrimaryKeyRestrictions(CType ctype)
+ {
+ this.ctype = ctype;
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound b, QueryOptions options) throws InvalidRequestException
+ {
- return values(options);
++ return values(cfm, options);
+ }
+
+ @Override
+ public final boolean isEmpty()
+ {
+ return getColumnDefs().isEmpty();
+ }
+
+ @Override
+ public final int size()
+ {
+ return getColumnDefs().size();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
index 385bfbc,0000000..4093780
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
@@@ -1,113 -1,0 +1,119 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+
+import org.apache.cassandra.cql3.ColumnSpecification;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
+
+/**
+ * Base class for <code>Restriction</code>s
+ */
+abstract class AbstractRestriction implements Restriction
+{
+ @Override
+ public boolean isOnToken()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isMultiColumn()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isIN()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isContains()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return true;
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
- return appendTo(builder, options);
++ return appendTo(cfm, builder, options);
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ return true;
++ }
++
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ return false;
+ }
+
+ protected static ByteBuffer validateIndexedValue(ColumnSpecification columnSpec,
+ ByteBuffer value)
+ throws InvalidRequestException
+ {
+ checkNotNull(value, "Unsupported null value for column %s", columnSpec.name);
+ checkBindValueSet(value, "Unsupported unset value for column %s", columnSpec.name);
+ checkFalse(value.remaining() > 0xFFFF, "Index expression values may not be larger than 64K");
+ return value;
+ }
+
+ /**
+ * Reverses the specified bound if the column type is a reversed one.
+ *
+ * @param columnDefinition the column definition
+ * @param bound the bound
+ * @return the bound reversed if the column type was a reversed one or the original bound
+ */
+ protected static Bound reverseBoundIfNeeded(ColumnDefinition columnDefinition, Bound bound)
+ {
+ return columnDefinition.isReversedType() ? bound.reverse() : bound;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
index 71855a0,0000000..76d0233
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
@@@ -1,185 -1,0 +1,192 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * A <code>PrimaryKeyRestrictions</code> which forwards all its method calls to another
+ * <code>PrimaryKeyRestrictions</code>. Subclasses should override one or more methods to modify the behavior
+ * of the backing <code>PrimaryKeyRestrictions</code> as desired per the decorator pattern.
+ */
+abstract class ForwardingPrimaryKeyRestrictions implements PrimaryKeyRestrictions
+{
+ /**
+ * Returns the backing delegate instance that methods are forwarded to.
+ * @return the backing delegate instance that methods are forwarded to.
+ */
+ protected abstract PrimaryKeyRestrictions getDelegate();
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return getDelegate().getFunctions();
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return getDelegate().getColumnDefs();
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return getDelegate().getFirstColumn();
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return getDelegate().getLastColumn();
+ }
+
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ {
+ return getDelegate().mergeWith(restriction);
+ }
+
+ @Override
+ public boolean hasSupportingIndex(SecondaryIndexManager secondaryIndexManager)
+ {
+ return getDelegate().hasSupportingIndex(secondaryIndexManager);
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return getDelegate().values(options);
++ return getDelegate().values(cfm, options);
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
- return getDelegate().appendTo(builder, options);
++ return getDelegate().appendTo(cfm, builder, options);
+ }
+
+ @Override
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return getDelegate().valuesAsComposites(options);
++ return getDelegate().valuesAsComposites(cfm, options);
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- return getDelegate().bounds(bound, options);
++ return getDelegate().bounds(cfm, bound, options);
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- return getDelegate().boundsAsComposites(bound, options);
++ return getDelegate().boundsAsComposites(cfm, bound, options);
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
- return getDelegate().appendBoundTo(builder, bound, options);
++ return getDelegate().appendBoundTo(cfm, builder, bound, options);
+ }
+
+ @Override
+ public boolean isInclusive(Bound bound)
+ {
+ return getDelegate().isInclusive(bound.reverse());
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return getDelegate().isEmpty();
+ }
+
+ @Override
+ public int size()
+ {
+ return getDelegate().size();
+ }
+
+ @Override
+ public boolean isOnToken()
+ {
+ return getDelegate().isOnToken();
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return getDelegate().isSlice();
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return getDelegate().isEQ();
+ }
+
+ @Override
+ public boolean isIN()
+ {
+ return getDelegate().isIN();
+ }
+
+ @Override
+ public boolean isContains()
+ {
+ return getDelegate().isContains();
+ }
+
+ @Override
+ public boolean isMultiColumn()
+ {
+ return getDelegate().isMultiColumn();
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return getDelegate().hasBound(b);
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ getDelegate().addIndexExpressionTo(expressions, indexManager, options);
+ }
++
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ return getDelegate().isNotReturningAnyRows(cfm, options);
++ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
index 51e2ce4,0000000..44f25ec
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
@@@ -1,515 -1,0 +1,553 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.Term.Terminal;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndex;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
+import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
+
+public abstract class MultiColumnRestriction extends AbstractRestriction
+{
+ /**
+ * The columns to which the restriction apply.
+ */
+ protected final List<ColumnDefinition> columnDefs;
+
+ public MultiColumnRestriction(List<ColumnDefinition> columnDefs)
+ {
+ this.columnDefs = columnDefs;
+ }
+
+ @Override
+ public boolean isMultiColumn()
+ {
+ return true;
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return columnDefs.get(0);
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return columnDefs.get(columnDefs.size() - 1);
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return columnDefs;
+ }
+
+ @Override
+ public final Restriction mergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ // We want to allow query like: (b,c) > (?, ?) AND b < ?
+ if (!otherRestriction.isMultiColumn()
+ && ((SingleColumnRestriction) otherRestriction).canBeConvertedToMultiColumnRestriction())
+ {
+ return doMergeWith(((SingleColumnRestriction) otherRestriction).toMultiColumnRestriction());
+ }
+
+ return doMergeWith(otherRestriction);
+ }
+
+ protected abstract Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException;
+
+ /**
+ * Returns the names of the columns that are specified within this <code>Restrictions</code> and the other one
+ * as a comma separated <code>String</code>.
+ *
+ * @param otherRestriction the other restrictions
+ * @return the names of the columns that are specified within this <code>Restrictions</code> and the other one
+ * as a comma separated <code>String</code>.
+ */
+ protected final String getColumnsInCommons(Restriction otherRestriction)
+ {
+ Set<ColumnDefinition> commons = new HashSet<>(getColumnDefs());
+ commons.retainAll(otherRestriction.getColumnDefs());
+ StringBuilder builder = new StringBuilder();
+ for (ColumnDefinition columnDefinition : commons)
+ {
+ if (builder.length() != 0)
+ builder.append(" ,");
+ builder.append(columnDefinition.name);
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public final boolean hasSupportingIndex(SecondaryIndexManager indexManager)
+ {
+ for (ColumnDefinition columnDef : columnDefs)
+ {
+ SecondaryIndex index = indexManager.getIndexForColumn(columnDef.name.bytes);
+ if (index != null && isSupportedBy(index))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if this type of restriction is supported for by the specified index.
+ * @param index the Secondary index
+ *
+ * @return <code>true</code> this type of restriction is supported by the specified index,
+ * <code>false</code> otherwise.
+ */
+ protected abstract boolean isSupportedBy(SecondaryIndex index);
+
+ public static class EQ extends MultiColumnRestriction
+ {
+ protected final Term value;
+
+ public EQ(List<ColumnDefinition> columnDefs, Term value)
+ {
+ super(columnDefs);
+ this.value = value;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return value.getFunctions();
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("EQ(%s)", value);
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes an Equal",
+ getColumnsInCommons(otherRestriction));
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return index.supportsOperator(Operator.EQ);
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ Tuples.Value t = ((Tuples.Value) value.bind(options));
+ List<ByteBuffer> values = t.getElements();
+ for (int i = 0, m = values.size(); i < m; i++)
+ {
+ builder.addElementToAll(values.get(i));
+ checkFalse(builder.containsNull(), "Invalid null value for column %s", columnDefs.get(i).name);
+ }
+ return builder;
+ }
+
+ @Override
+ public final void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ Tuples.Value t = ((Tuples.Value) value.bind(options));
+ List<ByteBuffer> values = t.getElements();
+
+ for (int i = 0, m = columnDefs.size(); i < m; i++)
+ {
+ ColumnDefinition columnDef = columnDefs.get(i);
+ ByteBuffer component = validateIndexedValue(columnDef, values.get(i));
+ expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, component));
+ }
+ }
++
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that
++ // any query with an EQ restriction containing an empty value will not return any results.
++ return !cfm.comparator.isCompound()
++ && !((Tuples.Value) value.bind(options)).getElements().get(0).hasRemaining();
++ }
+ }
+
+ public abstract static class IN extends MultiColumnRestriction
+ {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
- List<List<ByteBuffer>> splitInValues = splitValues(options);
++ List<List<ByteBuffer>> splitInValues = filterValuesIfNeeded(cfm, splitValues(options));
+ builder.addAllElementsToAll(splitInValues);
+
+ if (builder.containsNull())
+ throw invalidRequest("Invalid null value in condition for columns: %s", ColumnDefinition.toIdentifiers(columnDefs));
+ return builder;
+ }
+
++ private List<List<ByteBuffer>> filterValuesIfNeeded(CFMetaData cfm, List<List<ByteBuffer>> splitInValues)
++ {
++ if (cfm.comparator.isCompound())
++ return splitInValues;
++
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that we can
++ // ignore any IN value which is an empty byte buffer an which otherwise will trigger an error.
++
++ // As some List implementations do not support remove, we copy the list to be on the safe side.
++ List<List<ByteBuffer>> filteredValues = new ArrayList<>(splitInValues.size());
++ for (List<ByteBuffer> values : splitInValues)
++ {
++ if (values.get(0).hasRemaining())
++ filteredValues.add(values);
++ }
++ return filteredValues;
++ }
++
+ public IN(List<ColumnDefinition> columnDefs)
+ {
+ super(columnDefs);
+ }
+
+ @Override
+ public boolean isIN()
+ {
+ return true;
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes a IN",
+ getColumnsInCommons(otherRestriction));
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return index.supportsOperator(Operator.IN);
+ }
+
+ @Override
+ public final void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ List<List<ByteBuffer>> splitInValues = splitValues(options);
+ checkTrue(splitInValues.size() == 1, "IN restrictions are not supported on indexed columns");
+ List<ByteBuffer> values = splitInValues.get(0);
+
+ for (int i = 0, m = columnDefs.size(); i < m; i++)
+ {
+ ColumnDefinition columnDef = columnDefs.get(i);
+ ByteBuffer component = validateIndexedValue(columnDef, values.get(i));
+ expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, component));
+ }
+ }
+
+ protected abstract List<List<ByteBuffer>> splitValues(QueryOptions options) throws InvalidRequestException;
+ }
+
+ /**
+ * An IN restriction that has a set of terms for in values.
+ * For example: "SELECT ... WHERE (a, b, c) IN ((1, 2, 3), (4, 5, 6))" or "WHERE (a, b, c) IN (?, ?)"
+ */
+ public static class InWithValues extends MultiColumnRestriction.IN
+ {
+ protected final List<Term> values;
+
+ public InWithValues(List<ColumnDefinition> columnDefs, List<Term> values)
+ {
+ super(columnDefs);
+ this.values = values;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Terms.getFunctions(values);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("IN(%s)", values);
+ }
+
+ @Override
+ protected List<List<ByteBuffer>> splitValues(QueryOptions options) throws InvalidRequestException
+ {
+ List<List<ByteBuffer>> buffers = new ArrayList<>(values.size());
+ for (Term value : values)
+ {
+ Term.MultiItemTerminal term = (Term.MultiItemTerminal) value.bind(options);
+ buffers.add(term.getElements());
+ }
+ return buffers;
+ }
+ }
+
+ /**
+ * An IN restriction that uses a single marker for a set of IN values that are tuples.
+ * For example: "SELECT ... WHERE (a, b, c) IN ?"
+ */
+ public static class InWithMarker extends MultiColumnRestriction.IN
+ {
+ protected final AbstractMarker marker;
+
+ public InWithMarker(List<ColumnDefinition> columnDefs, AbstractMarker marker)
+ {
+ super(columnDefs);
+ this.marker = marker;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "IN ?";
+ }
+
+ @Override
+ protected List<List<ByteBuffer>> splitValues(QueryOptions options) throws InvalidRequestException
+ {
+ Tuples.InMarker inMarker = (Tuples.InMarker) marker;
+ Tuples.InValue inValue = inMarker.bind(options);
+ checkNotNull(inValue, "Invalid null value for IN restriction");
+ return inValue.getSplitValues();
+ }
+ }
+
+ public static class Slice extends MultiColumnRestriction
+ {
+ private final TermSlice slice;
+
+ public Slice(List<ColumnDefinition> columnDefs, Bound bound, boolean inclusive, Term term)
+ {
+ this(columnDefs, TermSlice.newInstance(bound, inclusive, term));
+ }
+
+ Slice(List<ColumnDefinition> columnDefs, TermSlice slice)
+ {
+ super(columnDefs);
+ this.slice = slice;
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return true;
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
+ boolean reversed = getFirstColumn().isReversedType();
+
+ EnumMap<Bound, List<ByteBuffer>> componentBounds = new EnumMap<Bound, List<ByteBuffer>>(Bound.class);
+ componentBounds.put(Bound.START, componentBounds(Bound.START, options));
+ componentBounds.put(Bound.END, componentBounds(Bound.END, options));
+
+ List<List<ByteBuffer>> toAdd = new ArrayList<>();
+ List<ByteBuffer> values = new ArrayList<>();
+
+ for (int i = 0, m = columnDefs.size(); i < m; i++)
+ {
+ ColumnDefinition column = columnDefs.get(i);
+ Bound b = reverseBoundIfNeeded(column, bound);
+
+ // For mixed order columns, we need to create additional slices when 2 columns are in reverse order
+ if (reversed != column.isReversedType())
+ {
+ reversed = column.isReversedType();
+ // As we are switching direction we need to add the current composite
+ toAdd.add(values);
+
+ // The new bound side has no value for this component. just stop
+ if (!hasComponent(b, i, componentBounds))
+ continue;
+
+ // The other side has still some components. We need to end the slice that we have just open.
+ if (hasComponent(b.reverse(), i, componentBounds))
+ toAdd.add(values);
+
+ // We need to rebuild where we are in this bound side
+ values = new ArrayList<ByteBuffer>();
+
+ List<ByteBuffer> vals = componentBounds.get(b);
+
+ int n = Math.min(i, vals.size());
+ for (int j = 0; j < n; j++)
+ {
+ ByteBuffer v = checkNotNull(vals.get(j),
+ "Invalid null value in condition for column %s",
+ columnDefs.get(j).name);
+ values.add(v);
+ }
+ }
+
+ if (!hasComponent(b, i, componentBounds))
+ continue;
+
+ ByteBuffer v = checkNotNull(componentBounds.get(b).get(i), "Invalid null value in condition for column %s", columnDefs.get(i).name);
+ values.add(v);
+ }
+ toAdd.add(values);
+
+ if (bound.isEnd())
+ Collections.reverse(toAdd);
+
+ return builder.addAllElementsToAll(toAdd);
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return slice.isSupportedBy(index);
+ }
+
+ @Override
+ public boolean hasBound(Bound bound)
+ {
+ return slice.hasBound(bound);
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return slice.getFunctions();
+ }
+
+ @Override
+ public boolean isInclusive(Bound bound)
+ {
+ return slice.isInclusive(bound);
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ checkTrue(otherRestriction.isSlice(),
+ "Column \"%s\" cannot be restricted by both an equality and an inequality relation",
+ getColumnsInCommons(otherRestriction));
+
+ if (!getFirstColumn().equals(otherRestriction.getFirstColumn()))
+ {
+ ColumnDefinition column = getFirstColumn().position() > otherRestriction.getFirstColumn().position()
+ ? getFirstColumn() : otherRestriction.getFirstColumn();
+
+ throw invalidRequest("Column \"%s\" cannot be restricted by two inequalities not starting with the same column",
+ column.name);
+ }
+
+ checkFalse(hasBound(Bound.START) && otherRestriction.hasBound(Bound.START),
+ "More than one restriction was found for the start bound on %s",
+ getColumnsInCommons(otherRestriction));
+ checkFalse(hasBound(Bound.END) && otherRestriction.hasBound(Bound.END),
+ "More than one restriction was found for the end bound on %s",
+ getColumnsInCommons(otherRestriction));
+
+ Slice otherSlice = (Slice) otherRestriction;
+ List<ColumnDefinition> newColumnDefs = columnDefs.size() >= otherSlice.columnDefs.size() ? columnDefs : otherSlice.columnDefs;
+
+ return new Slice(newColumnDefs, slice.merge(otherSlice.slice));
+ }
+
+ @Override
+ public final void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ throw invalidRequest("Multi-column slice restrictions cannot be used for filtering.");
+ }
+
+ @Override
+ public String toString()
+ {
+ return "SLICE" + slice;
+ }
+
+ /**
+ * Similar to bounds(), but returns one ByteBuffer per-component in the bound instead of a single
+ * ByteBuffer to represent the entire bound.
+ * @param b the bound type
+ * @param options the query options
+ * @return one ByteBuffer per-component in the bound
+ * @throws InvalidRequestException if the components cannot be retrieved
+ */
+ private List<ByteBuffer> componentBounds(Bound b, QueryOptions options) throws InvalidRequestException
+ {
+ if (!slice.hasBound(b))
+ return Collections.emptyList();
+
+ Terminal terminal = slice.bound(b).bind(options);
+
+ if (terminal instanceof Tuples.Value)
+ {
+ return ((Tuples.Value) terminal).getElements();
+ }
+
+ return Collections.singletonList(terminal.get(options.getProtocolVersion()));
+ }
+
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that
++ // any query with a slice restriction with an empty value for the END bound will not return any results.
++ return !cfm.comparator.isCompound()
++ && hasBound(Bound.END)
++ && !componentBounds(Bound.END, options).get(0).hasRemaining();
++ }
++
+ private boolean hasComponent(Bound b, int index, EnumMap<Bound, List<ByteBuffer>> componentBounds)
+ {
+ return componentBounds.get(b).size() > index;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
index e1cbc29,0000000..2549bdf
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
@@@ -1,405 -1,0 +1,417 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import com.google.common.collect.Lists;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.*;
+import org.apache.cassandra.db.composites.Composite.EOC;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * A set of single column restrictions on a primary key part (partition key or clustering key).
+ */
+final class PrimaryKeyRestrictionSet extends AbstractPrimaryKeyRestrictions implements Iterable<Restriction>
+{
+ /**
+ * The restrictions.
+ */
+ private final RestrictionSet restrictions;
+
+ /**
+ * <code>true</code> if the restrictions are corresponding to an EQ, <code>false</code> otherwise.
+ */
+ private boolean eq;
+
+ /**
+ * <code>true</code> if the restrictions are corresponding to an IN, <code>false</code> otherwise.
+ */
+ private boolean in;
+
+ /**
+ * <code>true</code> if the restrictions are corresponding to a Slice, <code>false</code> otherwise.
+ */
+ private boolean slice;
+
+ /**
+ * <code>true</code> if the restrictions are corresponding to a Contains, <code>false</code> otherwise.
+ */
+ private boolean contains;
+
+ public PrimaryKeyRestrictionSet(CType ctype)
+ {
+ super(ctype);
+ this.restrictions = new RestrictionSet();
+ this.eq = true;
+ }
+
+ private PrimaryKeyRestrictionSet(PrimaryKeyRestrictionSet primaryKeyRestrictions,
+ Restriction restriction) throws InvalidRequestException
+ {
+ super(primaryKeyRestrictions.ctype);
+ this.restrictions = primaryKeyRestrictions.restrictions.addRestriction(restriction);
+
+ if (restriction.isSlice() || primaryKeyRestrictions.isSlice())
+ this.slice = true;
+ else if (restriction.isContains() || primaryKeyRestrictions.isContains())
+ this.contains = true;
+ else if (restriction.isIN() || primaryKeyRestrictions.isIN())
+ this.in = true;
+ else
+ this.eq = true;
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return slice;
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return eq;
+ }
+
+ @Override
+ public boolean isIN()
+ {
+ return in;
+ }
+
+ @Override
+ public boolean isOnToken()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isContains()
+ {
+ return contains;
+ }
+
+ @Override
+ public boolean isMultiColumn()
+ {
+ return false;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return restrictions.getFunctions();
+ }
+
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction.isOnToken())
+ {
+ if (isEmpty())
+ return (PrimaryKeyRestrictions) restriction;
+
+ return new TokenFilter(this, (TokenRestriction) restriction);
+ }
+
+ return new PrimaryKeyRestrictionSet(this, restriction);
+ }
+
+ @Override
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return filterAndSort(appendTo(new CompositesBuilder(ctype), options).build());
++ return filterAndSort(appendTo(cfm, new CompositesBuilder(ctype), options).build());
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ for (Restriction r : restrictions)
+ {
- r.appendTo(builder, options);
++ r.appendTo(cfm, builder, options);
+ if (builder.hasMissingElements())
+ break;
+ }
+ return builder;
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
+ CompositesBuilder builder = new CompositesBuilder(ctype);
+ // The end-of-component of composite doesn't depend on whether the
+ // component type is reversed or not (i.e. the ReversedType is applied
+ // to the component comparator but not to the end-of-component itself),
+ // it only depends on whether the slice is reversed
+ int keyPosition = 0;
+ for (Restriction r : restrictions)
+ {
+ ColumnDefinition def = r.getFirstColumn();
+
+ if (keyPosition != def.position() || r.isContains())
+ break;
+
+ if (r.isSlice())
+ {
- r.appendBoundTo(builder, bound, options);
++ r.appendBoundTo(cfm, builder, bound, options);
+
+ // Since CASSANDRA-7281, the composites might not end with the same components and it is possible
+ // that one of the composites is an empty one. Unfortunatly, AbstractCType will always sort
+ // Composites.EMPTY before all the other components due to its EOC, even if it is not the desired
+ // behaviour in some cases. To avoid that problem the code will use normal composites for the empty
+ // ones until the composites are properly sorted. They will then be replaced by Composites.EMPTY as
+ // it is what is expected by the intra-node serialization.
+ // It is clearly a hack but it does not make a lot of sense to refactor 2.2 for that as the problem is
+ // already solved in 3.0.
+ List<Composite> composites = filterAndSort(setEocs(r, bound, builder.build()));
+ return Lists.transform(composites, new com.google.common.base.Function<Composite, Composite>()
+ {
+ @Override
+ public Composite apply(Composite composite)
+ {
+ return composite.isEmpty() ? Composites.EMPTY: composite;
+ }
+ });
+ }
+
- r.appendBoundTo(builder, bound, options);
++ r.appendBoundTo(cfm, builder, bound, options);
+
+ if (builder.hasMissingElements())
+ return Collections.emptyList();
+
+ keyPosition = r.getLastColumn().position() + 1;
+ }
+ // Means no relation at all or everything was an equal
+ // Note: if the builder is "full", there is no need to use the end-of-component bit. For columns selection,
+ // it would be harmless to do it. However, we use this method got the partition key too. And when a query
+ // with 2ndary index is done, and with the the partition provided with an EQ, we'll end up here, and in that
+ // case using the eoc would be bad, since for the random partitioner we have no guarantee that
+ // prefix.end() will sort after prefix (see #5240).
+ EOC eoc = !builder.hasRemaining() ? EOC.NONE : (bound.isEnd() ? EOC.END : EOC.START);
+ return filterAndSort(builder.buildWithEOC(eoc));
+ }
+
+ /**
+ * Removes duplicates and sort the specified composites.
+ *
+ * @param composites the composites to filter and sort
+ * @return the composites sorted and without duplicates
+ */
+ private List<Composite> filterAndSort(List<Composite> composites)
+ {
+ if (composites.size() <= 1)
+ return composites;
+
+ TreeSet<Composite> set = new TreeSet<Composite>(ctype);
+ set.addAll(composites);
+
+ return new ArrayList<>(set);
+ }
+
+ /**
+ * Sets EOCs for the composites returned by the specified slice restriction for the given bound.
+ *
+ * @param r the slice restriction
+ * @param bound the bound
+ * @param composites the composites
+ * @return the composites with their EOCs properly set
+ */
+ private List<Composite> setEocs(Restriction r, Bound bound, List<Composite> composites)
+ {
+ List<Composite> list = new ArrayList<>(composites.size());
+
+ // The first column of the slice might not be the first clustering column (e.g. clustering_0 = ? AND (clustering_1, clustering_2) >= (?, ?)
+ int offset = r.getFirstColumn().position();
+
+ for (int i = 0, m = composites.size(); i < m; i++)
+ {
+ Composite composite = composites.get(i);
+
+ // Handle the no bound case
+ if (composite.size() == offset)
+ {
+ list.add(composite.withEOC(bound.isEnd() ? EOC.END : EOC.START));
+ continue;
+ }
+
+ // In the case of mixed order columns, we will have some extra slices where the columns change directions.
+ // For example: if we have clustering_0 DESC and clustering_1 ASC a slice like (clustering_0, clustering_1) > (1, 2)
+ // will produce 2 slices: [EMPTY, 1.START] and [1.2.END, 1.END]
+ // So, the END bound will return 2 composite with the same values 1
+ if (composite.size() <= r.getLastColumn().position() && i < m - 1 && composite.equals(composites.get(i + 1)))
+ {
+ list.add(composite.withEOC(EOC.START));
+ list.add(composites.get(i++).withEOC(EOC.END));
+ continue;
+ }
+
+ // Handle the normal bounds
+ ColumnDefinition column = r.getColumnDefs().get(composite.size() - 1 - offset);
+ Bound b = reverseBoundIfNeeded(column, bound);
+
+ Composite.EOC eoc = eocFor(r, bound, b);
+ list.add(composite.withEOC(eoc));
+ }
+
+ return list;
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return Composites.toByteBuffers(valuesAsComposites(options));
++ return Composites.toByteBuffers(valuesAsComposites(cfm, options));
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound b, QueryOptions options) throws InvalidRequestException
+ {
- return Composites.toByteBuffers(boundsAsComposites(b, options));
++ return Composites.toByteBuffers(boundsAsComposites(cfm, b, options));
+ }
+
+ private static Composite.EOC eocFor(Restriction r, Bound eocBound, Bound inclusiveBound)
+ {
+ if (eocBound.isStart())
+ return r.isInclusive(inclusiveBound) ? Composite.EOC.NONE : Composite.EOC.END;
+
+ return r.isInclusive(inclusiveBound) ? Composite.EOC.END : Composite.EOC.START;
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ if (isEmpty())
+ return false;
+ return restrictions.lastRestriction().hasBound(b);
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ if (isEmpty())
+ return false;
+ return restrictions.lastRestriction().isInclusive(b);
+ }
+
+ @Override
+ public boolean hasSupportingIndex(SecondaryIndexManager indexManager)
+ {
+ return restrictions.hasSupportingIndex(indexManager);
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ Boolean clusteringColumns = null;
+ int position = 0;
+
+ for (Restriction restriction : restrictions)
+ {
+ ColumnDefinition columnDef = restriction.getFirstColumn();
+
+ // PrimaryKeyRestrictionSet contains only one kind of column, either partition key or clustering columns.
+ // Therefore we only need to check the column kind once. All the other columns will be of the same kind.
+ if (clusteringColumns == null)
+ clusteringColumns = columnDef.isClusteringColumn() ? Boolean.TRUE : Boolean.FALSE;
+
+ // We ignore all the clustering columns that can be handled by slices.
+ if (!clusteringColumns || handleInFilter(restriction, position) || restriction.hasSupportingIndex(indexManager))
+ {
+ restriction.addIndexExpressionTo(expressions, indexManager, options);
+ continue;
+ }
+
+ if (!restriction.isSlice())
+ position = restriction.getLastColumn().position() + 1;
+ }
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return restrictions.getColumnDefs();
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return restrictions.firstColumn();
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return restrictions.lastColumn();
+ }
+
+ public final boolean needsFiltering()
+ {
+ // Backported from ClusteringColumnRestrictions from CASSANDRA-11310 for 3.6
+ // As that suggests, this should only be called on clustering column
+ // and not partition key restrictions.
+ int position = 0;
+ for (Restriction restriction : restrictions)
+ {
+ if (handleInFilter(restriction, position))
+ return true;
+
+ if (!restriction.isSlice())
+ position = restriction.getLastColumn().position() + 1;
+ }
+
+ return false;
+ }
+
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ for (Restriction restriction : restrictions)
++ {
++ if (restriction.isNotReturningAnyRows(cfm, options))
++ return true;
++ }
++ return false;
++ }
++
+ private boolean handleInFilter(Restriction restriction, int index)
+ {
+ return restriction.isContains() || index != restriction.getFirstColumn().position();
+ }
+
+ public Iterator<Restriction> iterator()
+ {
+ return restrictions.iterator();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
index 7d7b492,0000000..5e5e3f5
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
@@@ -1,44 -1,0 +1,45 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * A set of restrictions on a primary key part (partition key or clustering key).
+ *
+ */
+interface PrimaryKeyRestrictions extends Restriction, Restrictions
+{
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException;
+
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException;
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException;
+
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException;
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException;
+
- public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws InvalidRequestException;
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound b, QueryOptions options) throws InvalidRequestException;
+
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException;
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException;
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
index f0ea6a7,0000000..9df100a
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
@@@ -1,138 -1,0 +1,150 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * A restriction/clause on a column.
+ * The goal of this class being to group all conditions for a column in a SELECT.
+ *
+ * <p>Implementation of this class must be immutable. See {@link #mergeWith(Restriction)} for more explanation.</p>
+ */
+public interface Restriction
+{
+ public boolean isOnToken();
+ public boolean isSlice();
+ public boolean isEQ();
+ public boolean isIN();
+ public boolean isContains();
+ public boolean isMultiColumn();
+
+ /**
+ * Returns the definition of the first column.
+ * @return the definition of the first column.
+ */
+ public ColumnDefinition getFirstColumn();
+
+ /**
+ * Returns the definition of the last column.
+ * @return the definition of the last column.
+ */
+ public ColumnDefinition getLastColumn();
+
+ /**
+ * Returns the column definitions in position order.
+ * @return the column definitions in position order.
+ */
+ public List<ColumnDefinition> getColumnDefs();
+
+ /**
+ * Return an Iterable over all of the functions (both native and user-defined) used by any component
+ * of the restriction
+ * @return functions all functions found (may contain duplicates)
+ */
+ public Iterable<Function> getFunctions();
+
+ /**
+ * Checks if the specified bound is set or not.
+ * @param b the bound type
+ * @return <code>true</code> if the specified bound is set, <code>false</code> otherwise
+ */
+ public boolean hasBound(Bound b);
+
+ /**
+ * Checks if the specified bound is inclusive or not.
+ * @param b the bound type
+ * @return <code>true</code> if the specified bound is inclusive, <code>false</code> otherwise
+ */
+ public boolean isInclusive(Bound b);
+
+ /**
+ * Merges this restriction with the specified one.
+ *
+ * <p>Restriction are immutable. Therefore merging two restrictions result in a new one.
+ * The reason behind this choice is that it allow a great flexibility in the way the merging can done while
+ * preventing any side effect.</p>
+ *
+ * @param otherRestriction the restriction to merge into this one
+ * @return the restriction resulting of the merge
+ * @throws InvalidRequestException if the restrictions cannot be merged
+ */
+ public Restriction mergeWith(Restriction otherRestriction) throws InvalidRequestException;
+
+ /**
+ * Check if the restriction is on indexed columns.
+ *
+ * @param indexManager the index manager
+ * @return <code>true</code> if the restriction is on indexed columns, <code>false</code>
+ */
+ public boolean hasSupportingIndex(SecondaryIndexManager indexManager);
+
+ /**
+ * Adds to the specified list the <code>IndexExpression</code>s corresponding to this <code>Restriction</code>.
+ *
+ * @param expressions the list to add the <code>IndexExpression</code>s to
+ * @param indexManager the secondary index manager
+ * @param options the query options
+ * @throws InvalidRequestException if this <code>Restriction</code> cannot be converted into
+ * <code>IndexExpression</code>s
+ */
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options)
+ throws InvalidRequestException;
+
+ /**
+ * Appends the values of this <code>Restriction</code> to the specified builder.
+ *
++ * @param cfm the table metadata
+ * @param builder the <code>CompositesBuilder</code> to append to.
+ * @param options the query options
+ * @return the <code>CompositesBuilder</code>
+ */
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options);
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options);
+
+ /**
+ * Appends the values of the <code>Restriction</code> for the specified bound to the specified builder.
+ *
++ * @param cfm the table metadata
+ * @param builder the <code>CompositesBuilder</code> to append to.
+ * @param bound the bound
+ * @param options the query options
+ * @return the <code>CompositesBuilder</code>
+ */
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options);
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options);
++
++ /**
++ * Checks if this restriction will prevent the query to return any rows.
++ *
++ * @param cfm the table metadata
++ * @param options the query options
++ * @return {@code true} if this restriction will prevent the query to return any rows, {@false} otherwise
++ */
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options);
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/ReversedPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/ReversedPrimaryKeyRestrictions.java
index 9b33161,0000000..9107acd
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/ReversedPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/ReversedPrimaryKeyRestrictions.java
@@@ -1,77 -1,0 +1,78 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * <code>PrimaryKeyRestrictions</code> decorator that reverse the slices.
+ */
+final class ReversedPrimaryKeyRestrictions extends ForwardingPrimaryKeyRestrictions
+{
+ /**
+ * The decorated restrictions.
+ */
+ private PrimaryKeyRestrictions restrictions;
+
+ public ReversedPrimaryKeyRestrictions(PrimaryKeyRestrictions restrictions)
+ {
+ this.restrictions = restrictions;
+ }
+
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ {
+ return new ReversedPrimaryKeyRestrictions(this.restrictions.mergeWith(restriction));
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- List<ByteBuffer> buffers = restrictions.bounds(bound.reverse(), options);
++ List<ByteBuffer> buffers = restrictions.bounds(cfm, bound.reverse(), options);
+ Collections.reverse(buffers);
+ return buffers;
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- List<Composite> composites = restrictions.boundsAsComposites(bound.reverse(), options);
++ List<Composite> composites = restrictions.boundsAsComposites(cfm, bound.reverse(), options);
+ Collections.reverse(composites);
+ return composites;
+ }
+
+ @Override
+ public boolean isInclusive(Bound bound)
+ {
+ return this.restrictions.isInclusive(bound.reverse());
+ }
+
+ @Override
+ protected PrimaryKeyRestrictions getDelegate()
+ {
+ return this.restrictions;
+ }
+}
[06/12] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by bl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
index 735a2e2,0000000..1f4960b
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
@@@ -1,626 -1,0 +1,669 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import com.google.common.collect.Iterables;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.Term.Terminal;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndex;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.db.marshal.CompositeType;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
+import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
+
+public abstract class SingleColumnRestriction extends AbstractRestriction
+{
+ /**
+ * The definition of the column to which apply the restriction.
+ */
+ protected final ColumnDefinition columnDef;
+
+ public SingleColumnRestriction(ColumnDefinition columnDef)
+ {
+ this.columnDef = columnDef;
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return Collections.singletonList(columnDef);
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return columnDef;
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return columnDef;
+ }
+
+ @Override
+ public boolean hasSupportingIndex(SecondaryIndexManager indexManager)
+ {
+ SecondaryIndex index = indexManager.getIndexForColumn(columnDef.name.bytes);
+ return index != null && isSupportedBy(index);
+ }
+
+ @Override
+ public final Restriction mergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ // We want to allow query like: b > ? AND (b,c) < (?, ?)
+ if (otherRestriction.isMultiColumn() && canBeConvertedToMultiColumnRestriction())
+ {
+ return toMultiColumnRestriction().mergeWith(otherRestriction);
+ }
+
+ return doMergeWith(otherRestriction);
+ }
+
+ protected abstract Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException;
+
+ /**
+ * Converts this <code>SingleColumnRestriction</code> into a {@link MultiColumnRestriction}
+ *
+ * @return the <code>MultiColumnRestriction</code> corresponding to this
+ */
+ abstract MultiColumnRestriction toMultiColumnRestriction();
+
+ /**
+ * Checks if this <code>Restriction</code> can be converted into a {@link MultiColumnRestriction}
+ *
+ * @return <code>true</code> if this <code>Restriction</code> can be converted into a
+ * {@link MultiColumnRestriction}, <code>false</code> otherwise.
+ */
+ boolean canBeConvertedToMultiColumnRestriction()
+ {
+ return true;
+ }
+
+ /**
+ * Check if this type of restriction is supported by the specified index.
+ *
+ * @param index the Secondary index
+ * @return <code>true</code> this type of restriction is supported by the specified index,
+ * <code>false</code> otherwise.
+ */
+ protected abstract boolean isSupportedBy(SecondaryIndex index);
+
+ public static final class EQ extends SingleColumnRestriction
+ {
+ private final Term value;
+
+ public EQ(ColumnDefinition columnDef, Term value)
+ {
+ super(columnDef);
+ this.value = value;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return value.getFunctions();
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return true;
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ return new MultiColumnRestriction.EQ(Collections.singletonList(columnDef), value);
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ ByteBuffer buffer = validateIndexedValue(columnDef, value.bindAndGet(options));
+ expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, buffer));
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ builder.addElementToAll(value.bindAndGet(options));
+ checkFalse(builder.containsNull(), "Invalid null value in condition for column %s", columnDef.name);
+ checkFalse(builder.containsUnset(), "Invalid unset value for column %s", columnDef.name);
+ return builder;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("EQ(%s)", value);
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes an Equal", columnDef.name);
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return index.supportsOperator(Operator.EQ);
+ }
++
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ assert columnDef.isClusteringColumn();
++
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that
++ // any query with an EQ restriction containing an empty value will not return any results.
++ return !cfm.comparator.isCompound() && !value.bindAndGet(options).hasRemaining();
++ }
+ }
+
+ public static abstract class IN extends SingleColumnRestriction
+ {
+ public IN(ColumnDefinition columnDef)
+ {
+ super(columnDef);
+ }
+
+ @Override
+ public final boolean isIN()
+ {
+ return true;
+ }
+
+ @Override
+ public final Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes a IN", columnDef.name);
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
- builder.addEachElementToAll(getValues(options));
++ List<ByteBuffer> values = filterValuesIfNeeded(cfm, getValues(options));
++
++ builder.addEachElementToAll(values);
+ checkFalse(builder.containsNull(), "Invalid null value in condition for column %s", columnDef.name);
+ checkFalse(builder.containsUnset(), "Invalid unset value for column %s", columnDef.name);
+ return builder;
+ }
+
++ private List<ByteBuffer> filterValuesIfNeeded(CFMetaData cfm, List<ByteBuffer> values)
++ {
++ if (!columnDef.isClusteringColumn() || cfm.comparator.isCompound())
++ return values;
++
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that we can
++ // ignore any IN value which is an empty byte buffer an which otherwise will trigger an error.
++
++ // As some List implementations do not support remove, we copy the list to be on the safe side.
++ List<ByteBuffer> filteredValues = new ArrayList<>(values.size());
++ for (ByteBuffer value : values)
++ {
++ if (value.hasRemaining())
++ filteredValues.add(value);
++ }
++ return filteredValues;
++ }
++
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ List<ByteBuffer> values = getValues(options);
+ checkTrue(values.size() == 1, "IN restrictions are not supported on indexed columns");
+
+ ByteBuffer value = validateIndexedValue(columnDef, values.get(0));
+ expressions.add(new IndexExpression(columnDef.name.bytes, Operator.EQ, value));
+ }
+
+ @Override
+ protected final boolean isSupportedBy(SecondaryIndex index)
+ {
+ return index.supportsOperator(Operator.IN);
+ }
+
+ protected abstract List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException;
+ }
+
+ public static class InWithValues extends IN
+ {
+ protected final List<Term> values;
+
+ public InWithValues(ColumnDefinition columnDef, List<Term> values)
+ {
+ super(columnDef);
+ this.values = values;
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ return new MultiColumnRestriction.InWithValues(Collections.singletonList(columnDef), values);
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Terms.getFunctions(values);
+ }
+
+ @Override
+ protected List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException
+ {
+ List<ByteBuffer> buffers = new ArrayList<>(values.size());
+ for (Term value : values)
+ buffers.add(value.bindAndGet(options));
+ return buffers;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("IN(%s)", values);
+ }
+ }
+
+ public static class InWithMarker extends IN
+ {
+ protected final AbstractMarker marker;
+
+ public InWithMarker(ColumnDefinition columnDef, AbstractMarker marker)
+ {
+ super(columnDef);
+ this.marker = marker;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Collections.emptySet();
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ return new MultiColumnRestriction.InWithMarker(Collections.singletonList(columnDef), marker);
+ }
+
+ @Override
+ protected List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException
+ {
+ Terminal term = marker.bind(options);
+ checkNotNull(term, "Invalid null value for column %s", columnDef.name);
+ checkFalse(term == Constants.UNSET_VALUE, "Invalid unset value for column %s", columnDef.name);
+ Term.MultiItemTerminal lval = (Term.MultiItemTerminal) term;
+ return lval.getElements();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "IN ?";
+ }
+ }
+
+ public static final class Slice extends SingleColumnRestriction
+ {
+ private final TermSlice slice;
+
+ public Slice(ColumnDefinition columnDef, Bound bound, boolean inclusive, Term term)
+ {
+ super(columnDef);
+ slice = TermSlice.newInstance(bound, inclusive, term);
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return slice.getFunctions();
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ return new MultiColumnRestriction.Slice(Collections.singletonList(columnDef), slice);
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return true;
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return slice.hasBound(b);
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
+ Bound b = reverseBoundIfNeeded(getFirstColumn(), bound);
+
+ if (!hasBound(b))
+ return builder;
+
+ ByteBuffer value = slice.bound(b).bindAndGet(options);
+ checkBindValueSet(value, "Invalid unset value for column %s", columnDef.name);
+ return builder.addElementToAll(value);
+
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ return slice.isInclusive(b);
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ checkTrue(otherRestriction.isSlice(),
+ "Column \"%s\" cannot be restricted by both an equality and an inequality relation",
+ columnDef.name);
+
+ SingleColumnRestriction.Slice otherSlice = (SingleColumnRestriction.Slice) otherRestriction;
+
+ checkFalse(hasBound(Bound.START) && otherSlice.hasBound(Bound.START),
+ "More than one restriction was found for the start bound on %s", columnDef.name);
+
+ checkFalse(hasBound(Bound.END) && otherSlice.hasBound(Bound.END),
+ "More than one restriction was found for the end bound on %s", columnDef.name);
+
+ return new Slice(columnDef, slice.merge(otherSlice.slice));
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ for (Bound b : Bound.values())
+ {
+ if (hasBound(b))
+ {
+ ByteBuffer value = validateIndexedValue(columnDef, slice.bound(b).bindAndGet(options));
+ Operator op = slice.getIndexOperator(b);
+ // If the underlying comparator for name is reversed, we need to reverse the IndexOperator: user operation
+ // always refer to the "forward" sorting even if the clustering order is reversed, but the 2ndary code does
+ // use the underlying comparator as is.
+ op = columnDef.isReversedType() ? op.reverse() : op;
+ expressions.add(new IndexExpression(columnDef.name.bytes, op, value));
+ }
+ }
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ return slice.isSupportedBy(index);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("SLICE%s", slice);
+ }
+
++ @Override
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ assert columnDef.isClusteringColumn();
++
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, we know that
++ // any query with a slice restriction with an empty value for the END bound will not return any results.
++ return !cfm.comparator.isCompound()
++ && hasBound(Bound.END)
++ && !slice.bound(Bound.END).bindAndGet(options).hasRemaining();
++ }
++
+ private Slice(ColumnDefinition columnDef, TermSlice slice)
+ {
+ super(columnDef);
+ this.slice = slice;
+ }
+ }
+
+ // This holds CONTAINS, CONTAINS_KEY, and map[key] = value restrictions because we might want to have any combination of them.
+ public static final class Contains extends SingleColumnRestriction
+ {
+ private List<Term> values = new ArrayList<>(); // for CONTAINS
+ private List<Term> keys = new ArrayList<>(); // for CONTAINS_KEY
+ private List<Term> entryKeys = new ArrayList<>(); // for map[key] = value
+ private List<Term> entryValues = new ArrayList<>(); // for map[key] = value
+
+ public Contains(ColumnDefinition columnDef, Term t, boolean isKey)
+ {
+ super(columnDef);
+ if (isKey)
+ keys.add(t);
+ else
+ values.add(t);
+ }
+
+ public Contains(ColumnDefinition columnDef, Term mapKey, Term mapValue)
+ {
+ super(columnDef);
+ entryKeys.add(mapKey);
+ entryValues.add(mapValue);
+ }
+
+ @Override
+ MultiColumnRestriction toMultiColumnRestriction()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ boolean canBeConvertedToMultiColumnRestriction()
+ {
+ return false;
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isContains()
+ {
+ return true;
+ }
+
+ @Override
+ public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ checkTrue(otherRestriction.isContains(),
+ "Collection column %s can only be restricted by CONTAINS, CONTAINS KEY, or map-entry equality",
+ columnDef.name);
+
+ SingleColumnRestriction.Contains newContains = new Contains(columnDef);
+
+ copyKeysAndValues(this, newContains);
+ copyKeysAndValues((Contains) otherRestriction, newContains);
+
+ return newContains;
+ }
+
+ @Override
+ public void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options)
+ throws InvalidRequestException
+ {
+ addExpressionsFor(expressions, bindAndGet(values, options), Operator.CONTAINS);
+ addExpressionsFor(expressions, bindAndGet(keys, options), Operator.CONTAINS_KEY);
+ addExpressionsFor(expressions, entries(options), Operator.EQ);
+ }
+
+ private void addExpressionsFor(List<IndexExpression> target, List<ByteBuffer> values,
+ Operator op) throws InvalidRequestException
+ {
+ for (ByteBuffer value : values)
+ {
+ validateIndexedValue(columnDef, value);
+ target.add(new IndexExpression(columnDef.name.bytes, op, value));
+ }
+ }
+
+ @Override
+ protected boolean isSupportedBy(SecondaryIndex index)
+ {
+ boolean supported = false;
+
+ if (numberOfValues() > 0)
+ supported |= index.supportsOperator(Operator.CONTAINS);
+
+ if (numberOfKeys() > 0)
+ supported |= index.supportsOperator(Operator.CONTAINS_KEY);
+
+ if (numberOfEntries() > 0)
+ supported |= index.supportsOperator(Operator.EQ);
+
+ return supported;
+ }
+
+ public int numberOfValues()
+ {
+ return values.size();
+ }
+
+ public int numberOfKeys()
+ {
+ return keys.size();
+ }
+
+ public int numberOfEntries()
+ {
+ return entryKeys.size();
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return Iterables.concat(Terms.getFunctions(values),
+ Terms.getFunctions(keys),
+ Terms.getFunctions(entryKeys),
+ Terms.getFunctions(entryValues));
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("CONTAINS(values=%s, keys=%s, entryKeys=%s, entryValues=%s)", values, keys, entryKeys, entryValues);
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
++ public CompositesBuilder appendBoundTo(CFMetaData cfm, CompositesBuilder builder, Bound bound, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ private List<ByteBuffer> entries(QueryOptions options) throws InvalidRequestException
+ {
+ List<ByteBuffer> entryBuffers = new ArrayList<>(entryKeys.size());
+ List<ByteBuffer> keyBuffers = bindAndGet(entryKeys, options);
+ List<ByteBuffer> valueBuffers = bindAndGet(entryValues, options);
+ for (int i = 0; i < entryKeys.size(); i++)
+ {
+ if (valueBuffers.get(i) == null)
+ throw new InvalidRequestException("Unsupported null value for map-entry equality");
+ entryBuffers.add(CompositeType.build(keyBuffers.get(i), valueBuffers.get(i)));
+ }
+ return entryBuffers;
+ }
+
+ /**
+ * Binds the query options to the specified terms and returns the resulting values.
+ *
+ * @param terms the terms
+ * @param options the query options
+ * @return the value resulting from binding the query options to the specified terms
+ * @throws InvalidRequestException if a problem occurs while binding the query options
+ */
+ private static List<ByteBuffer> bindAndGet(List<Term> terms, QueryOptions options) throws InvalidRequestException
+ {
+ List<ByteBuffer> buffers = new ArrayList<>(terms.size());
+ for (Term value : terms)
+ buffers.add(value.bindAndGet(options));
+ return buffers;
+ }
+
+ /**
+ * Copies the keys and value from the first <code>Contains</code> to the second one.
+ *
+ * @param from the <code>Contains</code> to copy from
+ * @param to the <code>Contains</code> to copy to
+ */
+ private static void copyKeysAndValues(Contains from, Contains to)
+ {
+ to.values.addAll(from.values);
+ to.keys.addAll(from.keys);
+ to.entryKeys.addAll(from.entryKeys);
+ to.entryValues.addAll(from.entryValues);
+ }
+
+ private Contains(ColumnDefinition columnDef)
+ {
+ super(columnDef);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
index 1547210,0000000..8035877
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
@@@ -1,645 -1,0 +1,656 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.dht.*;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+import static org.apache.cassandra.config.ColumnDefinition.toIdentifiers;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
+import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
+import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
+
+/**
+ * The restrictions corresponding to the relations specified on the where-clause of CQL query.
+ */
+public final class StatementRestrictions
+{
+ public static final String REQUIRES_ALLOW_FILTERING_MESSAGE =
+ "Cannot execute this query as it might involve data filtering and " +
+ "thus may have unpredictable performance. If you want to execute " +
+ "this query despite the performance unpredictability, use ALLOW FILTERING";
+
+ /**
+ * The Column Family meta data
+ */
+ public final CFMetaData cfm;
+
+ /**
+ * Restrictions on partitioning columns
+ */
+ private PrimaryKeyRestrictions partitionKeyRestrictions;
+
+ /**
+ * Restrictions on clustering columns
+ */
+ private PrimaryKeyRestrictions clusteringColumnsRestrictions;
+
+ /**
+ * Restriction on non-primary key columns (i.e. secondary index restrictions)
+ */
+ private RestrictionSet nonPrimaryKeyRestrictions;
+
+ /**
+ * The restrictions used to build the index expressions
+ */
+ private final List<Restrictions> indexRestrictions = new ArrayList<>();
+
+ /**
+ * <code>true</code> if the secondary index need to be queried, <code>false</code> otherwise
+ */
+ private boolean usesSecondaryIndexing;
+
+ /**
+ * Specify if the query will return a range of partition keys.
+ */
+ private boolean isKeyRange;
+
+ /**
+ * Creates a new empty <code>StatementRestrictions</code>.
+ *
+ * @param cfm the column family meta data
+ * @return a new empty <code>StatementRestrictions</code>.
+ */
+ public static StatementRestrictions empty(CFMetaData cfm)
+ {
+ return new StatementRestrictions(cfm);
+ }
+
+ private StatementRestrictions(CFMetaData cfm)
+ {
+ this.cfm = cfm;
+ this.partitionKeyRestrictions = new PrimaryKeyRestrictionSet(cfm.getKeyValidatorAsCType());
+ this.clusteringColumnsRestrictions = new PrimaryKeyRestrictionSet(cfm.comparator);
+ this.nonPrimaryKeyRestrictions = new RestrictionSet();
+ }
+
+ public StatementRestrictions(CFMetaData cfm,
+ List<Relation> whereClause,
+ VariableSpecifications boundNames,
+ boolean selectsOnlyStaticColumns,
+ boolean selectACollection,
+ boolean allowFiltering)
+ {
+ this.cfm = cfm;
+ this.partitionKeyRestrictions = new PrimaryKeyRestrictionSet(cfm.getKeyValidatorAsCType());
+ this.clusteringColumnsRestrictions = new PrimaryKeyRestrictionSet(cfm.comparator);
+ this.nonPrimaryKeyRestrictions = new RestrictionSet();
+
+ /*
+ * WHERE clause. For a given entity, rules are: - EQ relation conflicts with anything else (including a 2nd EQ)
+ * - Can't have more than one LT(E) relation (resp. GT(E) relation) - IN relation are restricted to row keys
+ * (for now) and conflicts with anything else (we could allow two IN for the same entity but that doesn't seem
+ * very useful) - The value_alias cannot be restricted in any way (we don't support wide rows with indexed value
+ * in CQL so far)
+ */
+ for (Relation relation : whereClause)
+ addRestriction(relation.toRestriction(cfm, boundNames));
+
+ SecondaryIndexManager secondaryIndexManager = Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfName).indexManager;
+ boolean hasQueriableClusteringColumnIndex = clusteringColumnsRestrictions.hasSupportingIndex(secondaryIndexManager);
+ boolean hasQueriableIndex = hasQueriableClusteringColumnIndex
+ || partitionKeyRestrictions.hasSupportingIndex(secondaryIndexManager)
+ || nonPrimaryKeyRestrictions.hasSupportingIndex(secondaryIndexManager);
+
+ // At this point, the select statement if fully constructed, but we still have a few things to validate
+ processPartitionKeyRestrictions(hasQueriableIndex);
+
+ // Some but not all of the partition key columns have been specified;
+ // hence we need turn these restrictions into index expressions.
+ if (usesSecondaryIndexing)
+ indexRestrictions.add(partitionKeyRestrictions);
+
+ checkFalse(selectsOnlyStaticColumns && hasClusteringColumnsRestriction(),
+ "Cannot restrict clustering columns when selecting only static columns");
+
+ processClusteringColumnsRestrictions(hasQueriableIndex, selectACollection);
+
+ // Covers indexes on the first clustering column (among others).
+ if (isKeyRange && hasQueriableClusteringColumnIndex)
+ usesSecondaryIndexing = true;
+
+ usesSecondaryIndexing = usesSecondaryIndexing || clusteringColumnsRestrictions.isContains();
+
+ if (usesSecondaryIndexing)
+ indexRestrictions.add(clusteringColumnsRestrictions);
+
+ // Even if usesSecondaryIndexing is false at this point, we'll still have to use one if
+ // there is restrictions not covered by the PK.
+ if (!nonPrimaryKeyRestrictions.isEmpty())
+ {
+ if (!hasQueriableIndex)
+ {
+ // Filtering for non-index query is only supported for thrift static CFs
+ if (cfm.comparator.isDense() || cfm.comparator.isCompound())
+ throw invalidRequest("Predicates on non-primary-key columns (%s) are not yet supported for non secondary index queries",
+ Joiner.on(", ").join(toIdentifiers(nonPrimaryKeyRestrictions.getColumnDefs())));
+
+ if (!allowFiltering)
+ throw invalidRequest(REQUIRES_ALLOW_FILTERING_MESSAGE);
+ }
+ usesSecondaryIndexing = true;
+ indexRestrictions.add(nonPrimaryKeyRestrictions);
+ }
+
+ if (usesSecondaryIndexing)
+ validateSecondaryIndexSelections(selectsOnlyStaticColumns);
+ }
+
+ private void addRestriction(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction.isMultiColumn())
+ clusteringColumnsRestrictions = clusteringColumnsRestrictions.mergeWith(restriction);
+ else if (restriction.isOnToken())
+ partitionKeyRestrictions = partitionKeyRestrictions.mergeWith(restriction);
+ else
+ addSingleColumnRestriction((SingleColumnRestriction) restriction);
+ }
+
+ public Iterable<Function> getFunctions()
+ {
+ return Iterables.concat(partitionKeyRestrictions.getFunctions(),
+ clusteringColumnsRestrictions.getFunctions(),
+ nonPrimaryKeyRestrictions.getFunctions());
+ }
+
+ private void addSingleColumnRestriction(SingleColumnRestriction restriction) throws InvalidRequestException
+ {
+ ColumnDefinition def = restriction.columnDef;
+ if (def.isPartitionKey())
+ partitionKeyRestrictions = partitionKeyRestrictions.mergeWith(restriction);
+ else if (def.isClusteringColumn())
+ clusteringColumnsRestrictions = clusteringColumnsRestrictions.mergeWith(restriction);
+ else
+ nonPrimaryKeyRestrictions = nonPrimaryKeyRestrictions.addRestriction(restriction);
+ }
+
+ /**
+ * Checks if the restrictions on the partition key is an IN restriction.
+ *
+ * @return <code>true</code> the restrictions on the partition key is an IN restriction, <code>false</code>
+ * otherwise.
+ */
+ public boolean keyIsInRelation()
+ {
+ return partitionKeyRestrictions.isIN();
+ }
+
+ /**
+ * Checks if the query request a range of partition keys.
+ *
+ * @return <code>true</code> if the query request a range of partition keys, <code>false</code> otherwise.
+ */
+ public boolean isKeyRange()
+ {
+ return this.isKeyRange;
+ }
+
+ /**
+ * Checks if the secondary index need to be queried.
+ *
+ * @return <code>true</code> if the secondary index need to be queried, <code>false</code> otherwise.
+ */
+ public boolean usesSecondaryIndexing()
+ {
+ return this.usesSecondaryIndexing;
+ }
+
+ private void processPartitionKeyRestrictions(boolean hasQueriableIndex) throws InvalidRequestException
+ {
+ // If there is a queriable index, no special condition are required on the other restrictions.
+ // But we still need to know 2 things:
+ // - If we don't have a queriable index, is the query ok
+ // - Is it queriable without 2ndary index, which is always more efficient
+ // If a component of the partition key is restricted by a relation, all preceding
+ // components must have a EQ. Only the last partition key component can be in IN relation.
+ if (partitionKeyRestrictions.isOnToken())
+ isKeyRange = true;
+
+ if (hasPartitionKeyUnrestrictedComponents())
+ {
+ if (!partitionKeyRestrictions.isEmpty())
+ {
+ if (!hasQueriableIndex)
+ throw invalidRequest("Partition key parts: %s must be restricted as other parts are",
+ Joiner.on(", ").join(getPartitionKeyUnrestrictedComponents()));
+ }
+
+ isKeyRange = true;
+ usesSecondaryIndexing = hasQueriableIndex;
+ }
+ }
+
+ /**
+ * Checks if the partition key has some unrestricted components.
+ * @return <code>true</code> if the partition key has some unrestricted components, <code>false</code> otherwise.
+ */
+ private boolean hasPartitionKeyUnrestrictedComponents()
+ {
+ return partitionKeyRestrictions.size() < cfm.partitionKeyColumns().size();
+ }
+
+ public boolean hasPartitionKeyRestrictions()
+ {
+ return !partitionKeyRestrictions.isEmpty();
+ }
+
+ /**
+ * Checks if the restrictions contain any non-primary key restrictions
+ * @return <code>true</code> if the restrictions contain any non-primary key restrictions, <code>false</code> otherwise.
+ */
+ public boolean hasNonPrimaryKeyRestrictions()
+ {
+ return !nonPrimaryKeyRestrictions.isEmpty();
+ }
+
+ /**
+ * Returns the partition key components that are not restricted.
+ * @return the partition key components that are not restricted.
+ */
+ private List<ColumnIdentifier> getPartitionKeyUnrestrictedComponents()
+ {
+ List<ColumnDefinition> list = new ArrayList<>(cfm.partitionKeyColumns());
+ list.removeAll(partitionKeyRestrictions.getColumnDefs());
+ return ColumnDefinition.toIdentifiers(list);
+ }
+
+ /**
+ * Processes the clustering column restrictions.
+ *
+ * @param hasQueriableIndex <code>true</code> if some of the queried data are indexed, <code>false</code> otherwise
+ * @param selectACollection <code>true</code> if the query should return a collection column
+ * @throws InvalidRequestException if the request is invalid
+ */
+ private void processClusteringColumnsRestrictions(boolean hasQueriableIndex,
+ boolean selectACollection) throws InvalidRequestException
+ {
+ validateClusteringRestrictions(hasQueriableIndex);
+
+ checkFalse(clusteringColumnsRestrictions.isIN() && selectACollection,
+ "Cannot restrict clustering columns by IN relations when a collection is selected by the query");
+ checkFalse(clusteringColumnsRestrictions.isContains() && !hasQueriableIndex,
+ "Cannot restrict clustering columns by a CONTAINS relation without a secondary index");
+
+ if (hasClusteringColumnsRestriction() && clusteringRestrictionsNeedFiltering())
+ {
+ if (hasQueriableIndex)
+ {
+ usesSecondaryIndexing = true;
+ return;
+ }
+
+ List<ColumnDefinition> clusteringColumns = cfm.clusteringColumns();
+ List<ColumnDefinition> restrictedColumns = new LinkedList<>(clusteringColumnsRestrictions.getColumnDefs());
+
+ for (int i = 0, m = restrictedColumns.size(); i < m; i++)
+ {
+ ColumnDefinition clusteringColumn = clusteringColumns.get(i);
+ ColumnDefinition restrictedColumn = restrictedColumns.get(i);
+
+ if (!clusteringColumn.equals(restrictedColumn))
+ {
+ throw invalidRequest(
+ "PRIMARY KEY column \"%s\" cannot be restricted as preceding column \"%s\" is not restricted",
+ restrictedColumn.name,
+ clusteringColumn.name);
+ }
+ }
+ }
+ }
+
+ /**
+ * Validates whether or not restrictions are allowed for execution when secondary index is not used.
+ */
+ public final void validateClusteringRestrictions(boolean hasQueriableIndex)
+ {
+ assert clusteringColumnsRestrictions instanceof PrimaryKeyRestrictionSet;
+
+ // If there's a queriable index, filtering will take care of clustering restrictions
+ if (hasQueriableIndex)
+ return;
+
+ Iterator<Restriction> iter = ((PrimaryKeyRestrictionSet)clusteringColumnsRestrictions).iterator();
+ Restriction previousRestriction = null;
+
+ while (iter.hasNext())
+ {
+ Restriction restriction = iter.next();
+
+ if (previousRestriction != null)
+ {
+ ColumnDefinition lastRestrictionStart = previousRestriction.getFirstColumn();
+ ColumnDefinition newRestrictionStart = restriction.getFirstColumn();
+
+ if (previousRestriction.isSlice() && newRestrictionStart.position() > lastRestrictionStart.position())
+ throw invalidRequest("Clustering column \"%s\" cannot be restricted (preceding column \"%s\" is restricted by a non-EQ relation)",
+ newRestrictionStart.name,
+ lastRestrictionStart.name);
+ }
+ previousRestriction = restriction;
+ }
+ }
+
+ public final boolean clusteringRestrictionsNeedFiltering()
+ {
+ assert clusteringColumnsRestrictions instanceof PrimaryKeyRestrictionSet;
+ return ((PrimaryKeyRestrictionSet) clusteringColumnsRestrictions).needsFiltering();
+ }
+
+ public List<IndexExpression> getIndexExpressions(SecondaryIndexManager indexManager,
+ QueryOptions options) throws InvalidRequestException
+ {
+ if (!usesSecondaryIndexing || indexRestrictions.isEmpty())
+ return Collections.emptyList();
+
+ List<IndexExpression> expressions = new ArrayList<>();
+ for (Restrictions restrictions : indexRestrictions)
+ restrictions.addIndexExpressionTo(expressions, indexManager, options);
+
+ return expressions;
+ }
+
+ /**
+ * Returns the partition keys for which the data is requested.
+ *
+ * @param options the query options
+ * @return the partition keys for which the data is requested.
+ * @throws InvalidRequestException if the partition keys cannot be retrieved
+ */
+ public Collection<ByteBuffer> getPartitionKeys(final QueryOptions options) throws InvalidRequestException
+ {
- return partitionKeyRestrictions.values(options);
++ return partitionKeyRestrictions.values(cfm, options);
+ }
+
+ /**
+ * Returns the specified bound of the partition key.
+ *
+ * @param b the boundary type
+ * @param options the query options
+ * @return the specified bound of the partition key
+ * @throws InvalidRequestException if the boundary cannot be retrieved
+ */
+ private ByteBuffer getPartitionKeyBound(Bound b, QueryOptions options) throws InvalidRequestException
+ {
+ // Deal with unrestricted partition key components (special-casing is required to deal with 2i queries on the
+ // first
+ // component of a composite partition key).
+ if (hasPartitionKeyUnrestrictedComponents())
+ return ByteBufferUtil.EMPTY_BYTE_BUFFER;
+
+ // We deal with IN queries for keys in other places, so we know buildBound will return only one result
- return partitionKeyRestrictions.bounds(b, options).get(0);
++ return partitionKeyRestrictions.bounds(cfm, b, options).get(0);
+ }
+
+ /**
+ * Returns the partition key bounds.
+ *
+ * @param options the query options
+ * @return the partition key bounds
+ * @throws InvalidRequestException if the query is invalid
+ */
+ public AbstractBounds<RowPosition> getPartitionKeyBounds(QueryOptions options) throws InvalidRequestException
+ {
+ IPartitioner p = StorageService.getPartitioner();
+
+ if (partitionKeyRestrictions.isOnToken())
+ {
+ return getPartitionKeyBoundsForTokenRestrictions(p, options);
+ }
+
+ return getPartitionKeyBounds(p, options);
+ }
+
+ private AbstractBounds<RowPosition> getPartitionKeyBounds(IPartitioner p,
+ QueryOptions options) throws InvalidRequestException
+ {
+ ByteBuffer startKeyBytes = getPartitionKeyBound(Bound.START, options);
+ ByteBuffer finishKeyBytes = getPartitionKeyBound(Bound.END, options);
+
+ RowPosition startKey = RowPosition.ForKey.get(startKeyBytes, p);
+ RowPosition finishKey = RowPosition.ForKey.get(finishKeyBytes, p);
+
+ if (startKey.compareTo(finishKey) > 0 && !finishKey.isMinimum())
+ return null;
+
+ if (partitionKeyRestrictions.isInclusive(Bound.START))
+ {
+ return partitionKeyRestrictions.isInclusive(Bound.END)
+ ? new Bounds<>(startKey, finishKey)
+ : new IncludingExcludingBounds<>(startKey, finishKey);
+ }
+
+ return partitionKeyRestrictions.isInclusive(Bound.END)
+ ? new Range<>(startKey, finishKey)
+ : new ExcludingBounds<>(startKey, finishKey);
+ }
+
+ private AbstractBounds<RowPosition> getPartitionKeyBoundsForTokenRestrictions(IPartitioner p,
+ QueryOptions options)
+ throws InvalidRequestException
+ {
+ Token startToken = getTokenBound(Bound.START, options, p);
+ Token endToken = getTokenBound(Bound.END, options, p);
+
+ boolean includeStart = partitionKeyRestrictions.isInclusive(Bound.START);
+ boolean includeEnd = partitionKeyRestrictions.isInclusive(Bound.END);
+
+ /*
+ * If we ask SP.getRangeSlice() for (token(200), token(200)], it will happily return the whole ring.
+ * However, wrapping range doesn't really make sense for CQL, and we want to return an empty result in that
+ * case (CASSANDRA-5573). So special case to create a range that is guaranteed to be empty.
+ *
+ * In practice, we want to return an empty result set if either startToken > endToken, or both are equal but
+ * one of the bound is excluded (since [a, a] can contains something, but not (a, a], [a, a) or (a, a)).
+ * Note though that in the case where startToken or endToken is the minimum token, then this special case
+ * rule should not apply.
+ */
+ int cmp = startToken.compareTo(endToken);
+ if (!startToken.isMinimum() && !endToken.isMinimum()
+ && (cmp > 0 || (cmp == 0 && (!includeStart || !includeEnd))))
+ return null;
+
+ RowPosition start = includeStart ? startToken.minKeyBound() : startToken.maxKeyBound();
+ RowPosition end = includeEnd ? endToken.maxKeyBound() : endToken.minKeyBound();
+
+ return new Range<>(start, end);
+ }
+
+ private Token getTokenBound(Bound b, QueryOptions options, IPartitioner p) throws InvalidRequestException
+ {
+ if (!partitionKeyRestrictions.hasBound(b))
+ return p.getMinimumToken();
+
- ByteBuffer value = partitionKeyRestrictions.bounds(b, options).get(0);
++ ByteBuffer value = partitionKeyRestrictions.bounds(cfm, b, options).get(0);
+ checkNotNull(value, "Invalid null token value");
+ return p.getTokenFactory().fromByteArray(value);
+ }
+
+ /**
+ * Checks if the query does not contains any restriction on the clustering columns.
+ *
+ * @return <code>true</code> if the query does not contains any restriction on the clustering columns,
+ * <code>false</code> otherwise.
+ */
+ public boolean hasNoClusteringColumnsRestriction()
+ {
+ return clusteringColumnsRestrictions.isEmpty();
+ }
+
+ /**
+ * Checks if the query has some restrictions on the clustering columns.
+ *
+ * @return <code>true</code> if the query has some restrictions on the clustering columns,
+ * <code>false</code> otherwise.
+ */
+ public boolean hasClusteringColumnsRestriction()
+ {
+ return !clusteringColumnsRestrictions.isEmpty();
+ }
+
+ // For non-composite slices, we don't support internally the difference between exclusive and
+ // inclusive bounds, so we deal with it manually.
+ public boolean isNonCompositeSliceWithExclusiveBounds()
+ {
+ return !cfm.comparator.isCompound()
+ && clusteringColumnsRestrictions.isSlice()
+ && (!clusteringColumnsRestrictions.isInclusive(Bound.START) || !clusteringColumnsRestrictions.isInclusive(Bound.END));
+ }
+
+ /**
+ * Returns the requested clustering columns as <code>Composite</code>s.
+ *
+ * @param options the query options
+ * @return the requested clustering columns as <code>Composite</code>s
+ * @throws InvalidRequestException if the query is not valid
+ */
+ public List<Composite> getClusteringColumnsAsComposites(QueryOptions options) throws InvalidRequestException
+ {
- return clusteringColumnsRestrictions.valuesAsComposites(options);
++ return clusteringColumnsRestrictions.valuesAsComposites(cfm, options);
+ }
+
+ /**
+ * Returns the bounds (start or end) of the clustering columns as <code>Composites</code>.
+ *
+ * @param b the bound type
+ * @param options the query options
+ * @return the bounds (start or end) of the clustering columns as <code>Composites</code>
+ * @throws InvalidRequestException if the request is not valid
+ */
+ public List<Composite> getClusteringColumnsBoundsAsComposites(Bound b,
+ QueryOptions options) throws InvalidRequestException
+ {
- List<Composite> bounds = clusteringColumnsRestrictions.boundsAsComposites(b, options);
++ List<Composite> bounds = clusteringColumnsRestrictions.boundsAsComposites(cfm, b, options);
+ for (Composite c : bounds) {
+ if (!c.isEmpty())
+ QueryProcessor.validateComposite(c, cfm.comparator);
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns the bounds (start or end) of the clustering columns.
+ *
+ * @param b the bound type
+ * @param options the query options
+ * @return the bounds (start or end) of the clustering columns
+ * @throws InvalidRequestException if the request is not valid
+ */
+ public List<ByteBuffer> getClusteringColumnsBounds(Bound b, QueryOptions options) throws InvalidRequestException
+ {
- return clusteringColumnsRestrictions.bounds(b, options);
++ return clusteringColumnsRestrictions.bounds(cfm, b, options);
+ }
+
+ /**
+ * Checks if the bounds (start or end) of the clustering columns are inclusive.
+ *
+ * @param bound the bound type
+ * @return <code>true</code> if the bounds (start or end) of the clustering columns are inclusive,
+ * <code>false</code> otherwise
+ */
+ public boolean areRequestedBoundsInclusive(Bound bound)
+ {
+ return clusteringColumnsRestrictions.isInclusive(bound);
+ }
+
+ /**
+ * Checks if the query returns a range of columns.
+ *
+ * @return <code>true</code> if the query returns a range of columns, <code>false</code> otherwise.
+ */
+ public boolean isColumnRange()
+ {
+ // Due to CASSANDRA-5762, we always do a slice for CQL3 tables (not dense, composite).
+ // Static CF (non dense but non composite) never entails a column slice however
+ if (!cfm.comparator.isDense())
+ return cfm.comparator.isCompound();
+
+ // Otherwise (i.e. for compact table where we don't have a row marker anyway and thus don't care about
+ // CASSANDRA-5762),
+ // it is a range query if it has at least one the column alias for which no relation is defined or is not EQ.
+ return clusteringColumnsRestrictions.size() < cfm.clusteringColumns().size() || clusteringColumnsRestrictions.isSlice();
+ }
+
+ /**
+ * Checks if the query need to use filtering.
+ * @return <code>true</code> if the query need to use filtering, <code>false</code> otherwise.
+ */
+ public boolean needFiltering()
+ {
+ int numberOfRestrictedColumns = 0;
+ for (Restrictions restrictions : indexRestrictions)
+ numberOfRestrictedColumns += restrictions.size();
+
+ return numberOfRestrictedColumns > 1
+ || (numberOfRestrictedColumns == 0 && !clusteringColumnsRestrictions.isEmpty())
+ || (numberOfRestrictedColumns != 0
+ && nonPrimaryKeyRestrictions.hasMultipleContains());
+ }
+
+ private void validateSecondaryIndexSelections(boolean selectsOnlyStaticColumns) throws InvalidRequestException
+ {
+ checkFalse(keyIsInRelation(),
+ "Select on indexed columns and with IN clause for the PRIMARY KEY are not supported");
+ // When the user only select static columns, the intent is that we don't query the whole partition but just
+ // the static parts. But 1) we don't have an easy way to do that with 2i and 2) since we don't support index on
+ // static columns
+ // so far, 2i means that you've restricted a non static column, so the query is somewhat non-sensical.
+ checkFalse(selectsOnlyStaticColumns, "Queries using 2ndary indexes don't support selecting only static columns");
+ }
+
+ public void reverse()
+ {
+ clusteringColumnsRestrictions = new ReversedPrimaryKeyRestrictions(clusteringColumnsRestrictions);
+ }
++
++ /**
++ * Checks if the query will never return any rows.
++ *
++ * @param options the query options
++ * @return {@code true} if the query will never return any rows, {@false} otherwise
++ */
++ public boolean isNotReturningAnyRows(QueryOptions options)
++ {
++ return clusteringColumnsRestrictions.isNotReturningAnyRows(cfm, options);
++ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
index bd04610,0000000..18444ec
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java
@@@ -1,237 -1,0 +1,245 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.collect.BoundType;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.dht.IPartitioner;
+import org.apache.cassandra.dht.Token;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.service.StorageService;
+
+import static org.apache.cassandra.cql3.statements.Bound.END;
+import static org.apache.cassandra.cql3.statements.Bound.START;
+
+/**
+ * <code>Restriction</code> decorator used to merge non-token restriction and token restriction on partition keys.
+ */
+final class TokenFilter extends ForwardingPrimaryKeyRestrictions
+{
+ /**
+ * The decorated restriction
+ */
+ private PrimaryKeyRestrictions restrictions;
+
+ /**
+ * The restriction on the token
+ */
+ private TokenRestriction tokenRestriction;
+
+ /**
+ * The partitioner
+ */
+ private static final IPartitioner partitioner = StorageService.getPartitioner();
+
+ @Override
+ protected PrimaryKeyRestrictions getDelegate()
+ {
+ return restrictions;
+ }
+
+ @Override
+ public boolean isOnToken()
+ {
+ // if all partition key columns have non-token restrictions, we can simply use the token range to filter
+ // those restrictions and then ignore the token range
+ return restrictions.size() < tokenRestriction.size();
+ }
+
+ public TokenFilter(PrimaryKeyRestrictions restrictions, TokenRestriction tokenRestriction)
+ {
+ this.restrictions = restrictions;
+ this.tokenRestriction = tokenRestriction;
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
- return filter(restrictions.values(options), options);
++ return filter(cfm, restrictions.values(cfm, options), options);
+ }
+
+ @Override
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction.isOnToken())
+ return new TokenFilter(restrictions, (TokenRestriction) tokenRestriction.mergeWith(restriction));
+
+ return new TokenFilter(super.mergeWith(restriction), tokenRestriction);
+ }
+
+ @Override
+ public boolean isInclusive(Bound bound)
+ {
+ return tokenRestriction.isInclusive(bound);
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return tokenRestriction.hasBound(b);
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- return tokenRestriction.bounds(bound, options);
++ return tokenRestriction.bounds(cfm, bound, options);
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
- return tokenRestriction.boundsAsComposites(bound, options);
++ return tokenRestriction.boundsAsComposites(cfm, bound, options);
+ }
+
+ /**
+ * Filter the values returned by the restriction.
+ *
++ * @param cfm the table metadata
+ * @param values the values returned by the decorated restriction
+ * @param options the query options
+ * @return the values matching the token restriction
+ * @throws InvalidRequestException if the request is invalid
+ */
- private List<ByteBuffer> filter(List<ByteBuffer> values, QueryOptions options) throws InvalidRequestException
++ private List<ByteBuffer> filter(CFMetaData cfm, List<ByteBuffer> values, QueryOptions options) throws InvalidRequestException
+ {
- RangeSet<Token> rangeSet = tokenRestriction.isSlice() ? toRangeSet(tokenRestriction, options)
- : toRangeSet(tokenRestriction.values(options));
++ RangeSet<Token> rangeSet = tokenRestriction.isSlice() ? toRangeSet(cfm, tokenRestriction, options)
++ : toRangeSet(tokenRestriction.values(cfm, options));
+
+ return filterWithRangeSet(rangeSet, values);
+ }
+
+ /**
+ * Filter out the values for which the tokens are not included within the specified range.
+ *
+ * @param tokens the tokens range
+ * @param values the restricted values
+ * @return the values for which the tokens are not included within the specified range.
+ */
+ private static List<ByteBuffer> filterWithRangeSet(RangeSet<Token> tokens, List<ByteBuffer> values)
+ {
+ List<ByteBuffer> remaining = new ArrayList<>();
+
+ for (ByteBuffer value : values)
+ {
+ Token token = partitioner.getToken(value);
+
+ if (!tokens.contains(token))
+ continue;
+
+ remaining.add(value);
+ }
+ return remaining;
+ }
+
+ /**
+ * Converts the specified list into a range set.
+ *
+ * @param buffers the token restriction values
+ * @return the range set corresponding to the specified list
+ */
+ private static RangeSet<Token> toRangeSet(List<ByteBuffer> buffers)
+ {
+ ImmutableRangeSet.Builder<Token> builder = ImmutableRangeSet.builder();
+
+ for (ByteBuffer buffer : buffers)
+ builder.add(Range.singleton(deserializeToken(buffer)));
+
+ return builder.build();
+ }
+
+ /**
+ * Converts the specified slice into a range set.
+ *
++ * @param cfm the table metadata
+ * @param slice the slice to convert
+ * @param options the query option
+ * @return the range set corresponding to the specified slice
+ * @throws InvalidRequestException if the request is invalid
+ */
- private static RangeSet<Token> toRangeSet(TokenRestriction slice, QueryOptions options) throws InvalidRequestException
++ private static RangeSet<Token> toRangeSet(CFMetaData cfm, TokenRestriction slice, QueryOptions options) throws InvalidRequestException
+ {
+ if (slice.hasBound(START))
+ {
- Token start = deserializeToken(slice.bounds(START, options).get(0));
++ Token start = deserializeToken(slice.bounds(cfm, START, options).get(0));
+
+ BoundType startBoundType = toBoundType(slice.isInclusive(START));
+
+ if (slice.hasBound(END))
+ {
+ BoundType endBoundType = toBoundType(slice.isInclusive(END));
- Token end = deserializeToken(slice.bounds(END, options).get(0));
++ Token end = deserializeToken(slice.bounds(cfm, END, options).get(0));
+
+ if (start.equals(end) && (BoundType.OPEN == startBoundType || BoundType.OPEN == endBoundType))
+ return ImmutableRangeSet.of();
+
+ if (start.compareTo(end) <= 0)
+ return ImmutableRangeSet.of(Range.range(start,
+ startBoundType,
+ end,
+ endBoundType));
+
+ return ImmutableRangeSet.<Token> builder()
+ .add(Range.upTo(end, endBoundType))
+ .add(Range.downTo(start, startBoundType))
+ .build();
+ }
+ return ImmutableRangeSet.of(Range.downTo(start,
+ startBoundType));
+ }
- Token end = deserializeToken(slice.bounds(END, options).get(0));
++ Token end = deserializeToken(slice.bounds(cfm, END, options).get(0));
+ return ImmutableRangeSet.of(Range.upTo(end, toBoundType(slice.isInclusive(END))));
+ }
+
++ public boolean isNotReturningAnyRows(CFMetaData cfm, QueryOptions options)
++ {
++ return false;
++ }
++
+ /**
+ * Deserializes the token corresponding to the specified buffer.
+ *
+ * @param buffer the buffer
+ * @return the token corresponding to the specified buffer
+ */
+ private static Token deserializeToken(ByteBuffer buffer)
+ {
+ return partitioner.getTokenFactory().fromByteArray(buffer);
+ }
+
+ private static BoundType toBoundType(boolean inclusive)
+ {
+ return inclusive ? BoundType.CLOSED : BoundType.OPEN;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
index 3cd3304,0000000..97c55c4
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
@@@ -1,273 -1,0 +1,274 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.base.Joiner;
+
++import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.Term;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CType;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.composites.CompositesBuilder;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
+
+/**
+ * <code>Restriction</code> using the token function.
+ */
+public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
+{
+ /**
+ * The definition of the columns to which apply the token restriction.
+ */
+ protected final List<ColumnDefinition> columnDefs;
+
+ /**
+ * Creates a new <code>TokenRestriction</code> that apply to the specified columns.
+ *
+ * @param ctype the composite type
+ * @param columnDefs the definition of the columns to which apply the token restriction
+ */
+ public TokenRestriction(CType ctype, List<ColumnDefinition> columnDefs)
+ {
+ super(ctype);
+ this.columnDefs = columnDefs;
+ }
+
+ @Override
+ public boolean isOnToken()
+ {
+ return true;
+ }
+
+ @Override
+ public List<ColumnDefinition> getColumnDefs()
+ {
+ return columnDefs;
+ }
+
+ @Override
+ public ColumnDefinition getFirstColumn()
+ {
+ return columnDefs.get(0);
+ }
+
+ @Override
+ public ColumnDefinition getLastColumn()
+ {
+ return columnDefs.get(columnDefs.size() - 1);
+ }
+
+ @Override
+ public boolean hasSupportingIndex(SecondaryIndexManager secondaryIndexManager)
+ {
+ return false;
+ }
+
+ @Override
+ public final void addIndexExpressionTo(List<IndexExpression> expressions,
+ SecondaryIndexManager indexManager,
+ QueryOptions options)
+ {
+ throw new UnsupportedOperationException("Index expression cannot be created for token restriction");
+ }
+
+ @Override
- public CompositesBuilder appendTo(CompositesBuilder builder, QueryOptions options)
++ public CompositesBuilder appendTo(CFMetaData cfm, CompositesBuilder builder, QueryOptions options)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
++ public List<Composite> valuesAsComposites(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
- public List<Composite> boundsAsComposites(Bound bound, QueryOptions options) throws InvalidRequestException
++ public List<Composite> boundsAsComposites(CFMetaData cfm, Bound bound, QueryOptions options) throws InvalidRequestException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the column names as a comma separated <code>String</code>.
+ *
+ * @return the column names as a comma separated <code>String</code>.
+ */
+ protected final String getColumnNamesAsString()
+ {
+ return Joiner.on(", ").join(ColumnDefinition.toIdentifiers(columnDefs));
+ }
+
+ @Override
+ public final PrimaryKeyRestrictions mergeWith(Restriction otherRestriction) throws InvalidRequestException
+ {
+ if (!otherRestriction.isOnToken())
+ return new TokenFilter(toPrimaryKeyRestriction(otherRestriction), this);
+
+ return doMergeWith((TokenRestriction) otherRestriction);
+ }
+
+ /**
+ * Merges this restriction with the specified <code>TokenRestriction</code>.
+ * @param otherRestriction the <code>TokenRestriction</code> to merge with.
+ */
+ protected abstract PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction) throws InvalidRequestException;
+
+ /**
+ * Converts the specified restriction into a <code>PrimaryKeyRestrictions</code>.
+ *
+ * @param restriction the restriction to convert
+ * @return a <code>PrimaryKeyRestrictions</code>
+ * @throws InvalidRequestException if a problem occurs while converting the restriction
+ */
+ private PrimaryKeyRestrictions toPrimaryKeyRestriction(Restriction restriction) throws InvalidRequestException
+ {
+ if (restriction instanceof PrimaryKeyRestrictions)
+ return (PrimaryKeyRestrictions) restriction;
+
+ return new PrimaryKeyRestrictionSet(ctype).mergeWith(restriction);
+ }
+
+ public static final class EQ extends TokenRestriction
+ {
+ private final Term value;
+
+ public EQ(CType ctype, List<ColumnDefinition> columnDefs, Term value)
+ {
+ super(ctype, columnDefs);
+ this.value = value;
+ }
+
+ @Override
+ public boolean isEQ()
+ {
+ return true;
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return value.getFunctions();
+ }
+
+ @Override
+ protected PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction) throws InvalidRequestException
+ {
+ throw invalidRequest("%s cannot be restricted by more than one relation if it includes an Equal",
+ Joiner.on(", ").join(ColumnDefinition.toIdentifiers(columnDefs)));
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
+ return Collections.singletonList(value.bindAndGet(options));
+ }
+ }
+
+ public static class Slice extends TokenRestriction
+ {
+ private final TermSlice slice;
+
+ public Slice(CType ctype, List<ColumnDefinition> columnDefs, Bound bound, boolean inclusive, Term term)
+ {
+ super(ctype, columnDefs);
+ slice = TermSlice.newInstance(bound, inclusive, term);
+ }
+
+ @Override
+ public boolean isSlice()
+ {
+ return true;
+ }
+
+ @Override
- public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> values(CFMetaData cfm, QueryOptions options) throws InvalidRequestException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasBound(Bound b)
+ {
+ return slice.hasBound(b);
+ }
+
+ @Override
- public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws InvalidRequestException
++ public List<ByteBuffer> bounds(CFMetaData cfm, Bound b, QueryOptions options) throws InvalidRequestException
+ {
+ return Collections.singletonList(slice.bound(b).bindAndGet(options));
+ }
+
+ @Override
+ public Iterable<Function> getFunctions()
+ {
+ return slice.getFunctions();
+ }
+
+ @Override
+ public boolean isInclusive(Bound b)
+ {
+ return slice.isInclusive(b);
+ }
+
+ @Override
+ protected PrimaryKeyRestrictions doMergeWith(TokenRestriction otherRestriction)
+ throws InvalidRequestException
+ {
+ if (!otherRestriction.isSlice())
+ throw invalidRequest("Columns \"%s\" cannot be restricted by both an equality and an inequality relation",
+ getColumnNamesAsString());
+
+ TokenRestriction.Slice otherSlice = (TokenRestriction.Slice) otherRestriction;
+
+ if (hasBound(Bound.START) && otherSlice.hasBound(Bound.START))
+ throw invalidRequest("More than one restriction was found for the start bound on %s",
+ getColumnNamesAsString());
+
+ if (hasBound(Bound.END) && otherSlice.hasBound(Bound.END))
+ throw invalidRequest("More than one restriction was found for the end bound on %s",
+ getColumnNamesAsString());
+
+ return new Slice(ctype, columnDefs, slice.merge(otherSlice.slice));
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("SLICE%s", slice);
+ }
+
+ private Slice(CType ctype, List<ColumnDefinition> columnDefs, TermSlice slice)
+ {
+ super(ctype, columnDefs);
+ this.slice = slice;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index 5ffcc8a,f84188a..3d134b5
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@@ -311,23 -301,38 +311,23 @@@ public abstract class ModificationState
public List<ByteBuffer> buildPartitionKeyNames(QueryOptions options)
throws InvalidRequestException
{
- CBuilder keyBuilder = cfm.getKeyValidatorAsCType().builder();
- List<ByteBuffer> keys = new ArrayList<ByteBuffer>();
+ CompositesBuilder keyBuilder = new CompositesBuilder(cfm.getKeyValidatorAsCType());
for (ColumnDefinition def : cfm.partitionKeyColumns())
{
- Restriction r = processedKeys.get(def.name);
- if (r == null)
- throw new InvalidRequestException(String.format("Missing mandatory PRIMARY KEY part %s", def.name));
-
- List<ByteBuffer> values = r.values(options);
+ Restriction r = checkNotNull(processedKeys.get(def.name), "Missing mandatory PRIMARY KEY part %s", def.name);
- r.appendTo(keyBuilder, options);
++ r.appendTo(cfm, keyBuilder, options);
+ }
- if (keyBuilder.remainingCount() == 1)
- {
- for (ByteBuffer val : values)
- {
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value for partition key part %s", def.name));
- ByteBuffer key = keyBuilder.buildWith(val).toByteBuffer();
- ThriftValidation.validateKey(cfm, key);
- keys.add(key);
- }
- }
- else
+ return Lists.transform(filterAndSort(keyBuilder.build()), new com.google.common.base.Function<Composite, ByteBuffer>()
+ {
+ @Override
+ public ByteBuffer apply(Composite composite)
{
- if (values.size() != 1)
- throw new InvalidRequestException("IN is only supported on the last column of the partition key");
- ByteBuffer val = values.get(0);
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value for partition key part %s", def.name));
- keyBuilder.add(val);
+ ByteBuffer byteBuffer = composite.toByteBuffer();
+ ThriftValidation.validateKey(cfm, byteBuffer);
+ return byteBuffer;
}
- }
- return keys;
+ });
}
public Composite createClusteringPrefix(QueryOptions options)
@@@ -406,26 -411,15 +406,26 @@@
}
else
{
- r.appendTo(builder, options);
- List<ByteBuffer> values = r.values(options);
- assert values.size() == 1; // We only allow IN for row keys so far
- ByteBuffer val = values.get(0);
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
- builder.add(val);
++ r.appendTo(cfm, builder, options);
}
}
- return builder.build();
+ return builder.build().get(0); // We only allow IN for row keys so far
+ }
+
+ /**
+ * Removes duplicates and sort the specified composites.
+ *
+ * @param composites the composites to filter and sort
+ * @return the composites sorted and without duplicates
+ */
+ private List<Composite> filterAndSort(List<Composite> composites)
+ {
+ if (composites.size() <= 1)
+ return composites;
+
+ TreeSet<Composite> set = new TreeSet<Composite>(cfm.getKeyValidatorAsCType());
+ set.addAll(composites);
+ return new ArrayList<>(set);
}
protected ColumnDefinition getFirstEmptyKey()
[04/12] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by bl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
index bf5128f,0000000..bd5395a
mode 100644,000000..100644
--- a/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
+++ b/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
@@@ -1,1957 -1,0 +1,1957 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.restrictions;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.Term.MultiItemTerminal;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.ColumnFamilyType;
+import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.composites.Composite.EOC;
+import org.apache.cassandra.db.composites.Composites;
+import org.apache.cassandra.db.composites.CompoundSparseCellNameType;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.ReversedType;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class PrimaryKeyRestrictionSetTest
+{
+ @Test
+ public void testBoundsAsCompositesWithNoRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
+
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ }
+
+ /**
+ * Test 'clustering_0 = 1' with only one clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithOneEqRestrictionsAndOneClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
+
+ ByteBuffer clustering_0 = ByteBufferUtil.bytes(1);
+ Restriction eq = newSingleEq(cfMetaData, 0, clustering_0);
+
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), clustering_0, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), clustering_0, EOC.END);
+ }
+
+ /**
+ * Test 'clustering_1 = 1' with 2 clustering columns
+ */
+ @Test
+ public void testBoundsAsCompositesWithOneEqRestrictionsAndTwoClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer clustering_0 = ByteBufferUtil.bytes(1);
+ Restriction eq = newSingleEq(cfMetaData, 0, clustering_0);
+
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), clustering_0, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), clustering_0, EOC.END);
+ }
+
+ /**
+ * Test 'clustering_0 IN (1, 2, 3)' with only one clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithOneInRestrictionsAndOneClusteringColumn()
+ {
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ Restriction in = newSingleIN(cfMetaData, 0, value1, value2, value3);
+
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(in);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value2, EOC.START);
+ assertComposite(bounds.get(2), value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+ assertComposite(bounds.get(1), value2, EOC.END);
+ assertComposite(bounds.get(2), value3, EOC.END);
+ }
+
+ /**
+ * Test slice restriction (e.g 'clustering_0 > 1') with only one clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithSliceRestrictionsAndOneClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ Restriction slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+ Restriction slice2 = newSingleSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+ slice2 = newSingleSlice(cfMetaData, 0, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+ }
+
+ /**
+ * Test slice restriction (e.g 'clustering_0 > 1') with only one descending clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithSliceRestrictionsAndOneDescendingClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ Restriction slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+ Restriction slice2 = newSingleSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+ slice2 = newSingleSlice(cfMetaData, 0, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+ }
+
+ /**
+ * Test 'clustering_0 = 1 AND clustering_1 IN (1, 2, 3)'
+ */
+ @Test
+ public void testBoundsAsCompositesWithEqAndInRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ Restriction eq = newSingleEq(cfMetaData, 0, value1);
+ Restriction in = newSingleIN(cfMetaData, 1, value1, value2, value3);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(in);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.START);
+ assertComposite(bounds.get(2), value1, value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value1, EOC.END);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+ assertComposite(bounds.get(2), value1, value3, EOC.END);
+ }
+
+ /**
+ * Test equal and slice restrictions (e.g 'clustering_0 = 0 clustering_1 > 1')
+ */
+ @Test
+ public void testBoundsAsCompositesWithEqAndSliceRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+
+ Restriction eq = newSingleEq(cfMetaData, 0, value3);
+
+ Restriction slice = newSingleSlice(cfMetaData, 1, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.END);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.START, false, value1);
+ Restriction slice2 = newSingleSlice(cfMetaData, 1, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value2, EOC.START);
+
+ slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
+ slice2 = newSingleSlice(cfMetaData, 1, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value3, value2, EOC.END);
+ }
+
+ /**
+ * Test '(clustering_0, clustering_1) = (1, 2)' with two clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiEqRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ Restriction eq = newMultiEq(cfMetaData, 0, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(eq);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ }
+
+ /**
+ * Test '(clustering_0, clustering_1) IN ((1, 2), (2, 3))' with two clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiInRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ Restriction in = newMultiIN(cfMetaData, 0, asList(value1, value2), asList(value2, value3));
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(in);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value2, value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertComposite(bounds.get(1), value2, value3, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column in reverse
+ * order
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneDescendingClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions (e.g '(clustering_0, clustering_1) > (1, 2)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoClusteringColumn()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ // (clustering_0, clustering1) > (1, 2)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ // (clustering_0, clustering1) <= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1) < (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+ // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, value1, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with 2 descending clustering columns (e.g '(clustering_0, clustering_1) > (1, 2)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoDescendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ // (clustering_0, clustering1) > (1, 2)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1) <= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ // (clustering_0, clustering1) < (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+
+ // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value2, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with 1 descending clustering column and 1 ascending
+ * (e.g '(clustering_0, clustering_1) > (1, 2)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneDescendingAndOneAscendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ // (clustering_0, clustering1) > (1, 2)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0, clustering1) <= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1) < (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.END);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0) > (1) AND (clustering_0, clustering1) < (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+ assertComposite(bounds.get(1), value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value2, value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value2, EOC.START);
+ assertComposite(bounds.get(1), value2, EOC.END);
+ assertComposite(bounds.get(2), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value2, value1, EOC.END);
+ assertComposite(bounds.get(1), value1, EOC.START);
+ assertComposite(bounds.get(2), value1, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with 1 descending clustering column and 1 ascending
+ * (e.g '(clustering_0, clustering_1) > (1, 2)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneAscendingAndOneDescendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+ // (clustering_0, clustering1) > (1, 2)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1) <= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0, clustering1) < (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value2, EOC.START);
+
+ // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+ assertComposite(bounds.get(2), value2, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertComposite(bounds.get(1), value2, EOC.START);
+ assertComposite(bounds.get(2), value2, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with 2 ascending clustering column and 2 descending
+ * (e.g '(clustering_0, clustering1, clustering_3, clustering4) > (1, 2, 3, 4)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoAscendingAndTwoDescendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.DESC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ ByteBuffer value4 = ByteBufferUtil.bytes(4);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+ assertEmptyComposite(bounds.get(1));
+
+ // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+ Restriction eq = newSingleEq(cfMetaData, 0, value1);
+ slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+ restrictions = restrictions.mergeWith(eq);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
+ // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+ Restriction in = newSingleIN(cfMetaData, 0, value1, value2);
+ slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+ restrictions = restrictions.mergeWith(in);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+ assertComposite(bounds.get(2), value2, value2, EOC.START);
+ assertComposite(bounds.get(3), value2, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+ assertComposite(bounds.get(2), value2, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(3), value2, EOC.END);
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) <= (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) < (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4) AND (clustering_0, clustering_1) < (2, 3)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value3);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(1), value2, value3, EOC.START);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4) AND (clustering_0, clustering1, clustering_2, clustering_3) <= (4, 3, 2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value4, value3, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, EOC.END);
+ assertComposite(bounds.get(2), value4, value3, value2, value1, EOC.NONE);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
+ assertComposite(bounds.get(1), value4, value3, EOC.START);
+ assertComposite(bounds.get(2), value4, value3, EOC.END);
+ }
+
+ /**
+ * Test multi-column slice restrictions with ascending, descending, ascending and descending columns
+ * (e.g '(clustering_0, clustering1, clustering_3, clustering4) > (1, 2, 3, 4)')
+ */
+ @Test
+ public void testBoundsAsCompositesWithMultiSliceRestrictionsMixingAscendingDescendingClusteringColumns()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.DESC, Sort.ASC, Sort.DESC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ ByteBuffer value4 = ByteBufferUtil.bytes(4);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4)
+ Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(3));
+
+ // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+ Restriction eq = newSingleEq(cfMetaData, 0, value1);
+ slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+ restrictions = restrictions.mergeWith(eq);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(3, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+
+ // (clustering_0, clustering1) >= (1, 2)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(2, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(1));
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+ assertEmptyComposite(bounds.get(3));
+
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) <= (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, value4, EOC.NONE);
+ assertComposite(bounds.get(3), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) < (1, 2, 3, 4)
+ slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2, value3, value4);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertEmptyComposite(bounds.get(0));
+ assertComposite(bounds.get(1), value1, value2, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, value4, EOC.END);
+ assertComposite(bounds.get(3), value1, value2, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(4, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4) AND (clustering_0, clustering_1) < (2, 3)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+ Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value3);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(5, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+ assertComposite(bounds.get(4), value2, value3, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(5, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+ assertComposite(bounds.get(3), value2, EOC.START);
+ assertComposite(bounds.get(4), value2, EOC.END);
+
+ // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4) AND (clustering_0, clustering1, clustering_2, clustering_3) <= (4, 3, 2, 1)
+ slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+ slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value4, value3, value2, value1);
+ restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
- bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(7, bounds.size());
+ assertComposite(bounds.get(0), value1, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+ assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+ assertComposite(bounds.get(3), value1, EOC.END);
+ assertComposite(bounds.get(4), value4, value3, EOC.START);
+ assertComposite(bounds.get(5), value4, value3, value2, value1, EOC.NONE);
+ assertComposite(bounds.get(6), value4, value3, EOC.END);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComposites(cfMetaData, Bound.END, QueryOptions.DEFAULT);
+ assertEquals(7, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, EOC.START);
+ assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
+ assertComposite(bounds.get(2), value1, value2, EOC.END);
+ assertComposite(bounds.get(3), value4, EOC.START);
+ assertComposite(bounds.get(4), value4, value3, value2, EOC.START);
+ assertComposite(bounds.get(5), value4, value3, value2, EOC.END);
+ assertComposite(bounds.get(6), value4, EOC.END);
+ }
+
+ /**
+ * Test mixing single and multi equals restrictions (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3))
+ */
+ @Test
+ public void testBoundsAsCompositesWithSingleEqAndMultiEqRestrictions()
+ {
+ CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.ASC, Sort.ASC);
+
+ ByteBuffer value1 = ByteBufferUtil.bytes(1);
+ ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ ByteBuffer value3 = ByteBufferUtil.bytes(3);
+ ByteBuffer value4 = ByteBufferUtil.bytes(4);
+
+ // clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3)
+ Restriction singleEq = newSingleEq(cfMetaData, 0, value1);
+ Restriction multiEq = newMultiEq(cfMetaData, 1, value2, value3);
+ PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+ restrictions = restrictions.mergeWith(singleEq).mergeWith(multiEq);
+
- List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++ List<Composite> bounds = restrictions.boundsAsComposites(cfMetaData, Bound.START, QueryOptions.DEFAULT);
+ assertEquals(1, bounds.size());
+ assertComposite(bounds.get(0), value1, value2, value3, EOC.START);
+
- bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++ bounds = restrictions.boundsAsComp
<TRUNCATED>
[03/12] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by bl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
index 365abe0,4a54a9a..11d2462
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
@@@ -17,36 -17,26 +17,39 @@@
*/
package org.apache.cassandra.cql3.validation.entities;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import java.nio.ByteBuffer;
import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
-
-import org.apache.commons.lang.StringUtils;
-
-import org.junit.Test;
+import java.util.concurrent.CountDownLatch;
import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.db.ColumnFamily;
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.composites.CellName;
+import org.apache.cassandra.db.index.IndexNotAvailableException;
+import org.apache.cassandra.db.index.PerRowSecondaryIndex;
+import org.apache.cassandra.db.index.SecondaryIndexSearcher;
+import org.apache.cassandra.db.index.composites.CompositesSearcher;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.concurrent.OpOrder.Group;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Test;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+ import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+
public class SecondaryIndexTest extends CQLTester
{
private static final int TOO_BIG = 1024 * 65;
@@@ -655,134 -644,141 +658,273 @@@
}
@Test
+ public void testIndexQueriesWithIndexNotReady() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk int, ck int, value int, PRIMARY KEY (pk, ck))");
+
+ for (int i = 0; i < 10; i++)
+ for (int j = 0; j < 10; j++)
+ execute("INSERT INTO %s (pk, ck, value) VALUES (?, ?, ?)", i, j, i + j);
+
+ createIndex("CREATE CUSTOM INDEX testIndex ON %s (value) USING '" + IndexBlockingOnInitialization.class.getName()
+ + "'");
+ try
+ {
+ execute("SELECT value FROM %s WHERE value = 2");
+ fail();
+ }
+ catch (IndexNotAvailableException e)
+ {
+ assertTrue(true);
+ }
+ finally
+ {
+ execute("DROP index " + KEYSPACE + ".testIndex");
+ }
+ }
+
++ @Test
+ public void testWithEmptyRestrictionValueAndSecondaryIndex() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))");
+ createIndex("CREATE INDEX on %s(c)");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("1"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob('')) AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+ }
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"),
+ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithSecondaryIndexAndCompactTables() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c)) WITH COMPACT STORAGE");
+ assertInvalidMessage("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables",
+ "CREATE INDEX on %s(c)");
+
+ createTable("CREATE TABLE %s (pk blob PRIMARY KEY, v blob) WITH COMPACT STORAGE");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo123"), bytes("1"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo124"), EMPTY_BYTE_BUFFER);
+
+ assertRows(execute("SELECT * FROM %s WHERE v = textAsBlob('');"),
+ row(bytes("foo124"), EMPTY_BYTE_BUFFER));
+ }
++
+ /**
+ * Custom index used to test the behavior of the system when the index is not ready.
+ * As Custom indices cannot by <code>PerColumnSecondaryIndex</code> we use a <code>PerRowSecondaryIndex</code>
+ * to avoid the check but return a <code>CompositesSearcher</code>.
+ */
+ public static class IndexBlockingOnInitialization extends PerRowSecondaryIndex
+ {
+ private volatile CountDownLatch latch = new CountDownLatch(1);
+
+ @Override
+ public void index(ByteBuffer rowKey, ColumnFamily cf)
+ {
+ try
+ {
+ latch.await();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.interrupted();
+ }
+ }
+
+ @Override
+ public void delete(DecoratedKey key, Group opGroup)
+ {
+ }
+
+ @Override
+ public void init()
+ {
+ }
+
+ @Override
+ public void reload()
+ {
+ }
+
+ @Override
+ public void validateOptions() throws ConfigurationException
+ {
+ }
+
+ @Override
+ public String getIndexName()
+ {
+ return "testIndex";
+ }
+
+ @Override
+ protected SecondaryIndexSearcher createSecondaryIndexSearcher(Set<ByteBuffer> columns)
+ {
+ return new CompositesSearcher(baseCfs.indexManager, columns)
+ {
+ @Override
+ public boolean canHandleIndexClause(List<IndexExpression> clause)
+ {
+ return true;
+ }
+
+ @Override
+ public void validate(IndexExpression indexExpression) throws InvalidRequestException
+ {
+ }
+ };
+ }
+
+ @Override
+ public void forceBlockingFlush()
+ {
+ }
+
+ @Override
+ public ColumnFamilyStore getIndexCfs()
+ {
+ return baseCfs;
+ }
+
+ @Override
+ public void removeIndex(ByteBuffer columnName)
+ {
+ latch.countDown();
+ }
+
+ @Override
+ public void invalidate()
+ {
+ }
+
+ @Override
+ public void truncateBlocking(long truncatedAt)
+ {
+ }
+
+ @Override
+ public boolean indexes(CellName name)
+ {
+ return false;
+ }
+
+ @Override
+ public long estimateResultRows()
+ {
+ return 0;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
index f8ec13c,cf923bc..32d800a
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderByTest.java
@@@ -36,31 -35,31 +36,37 @@@ public class SelectOrderByTest extends
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 2, 2);
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0, 0, 0),
-- row(0, 1, 1),
-- row(0, 2, 2)
-- );
--
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(0, 2, 2),
-- row(0, 1, 1),
-- row(0, 0, 0)
-- );
--
-- // order by the only column in the selection
-- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
--
-- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
--
-- // order by a column not in the selection
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
--
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0, 0, 0),
++ row(0, 1, 1),
++ row(0, 2, 2)
++ );
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(0, 2, 2),
++ row(0, 1, 1),
++ row(0, 0, 0)
++ );
++
++ // order by the only column in the selection
++ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
++
++ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
++
++ // order by a column not in the selection
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
++
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
++ }
}
}
@@@ -74,22 -73,22 +80,28 @@@
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 2, 2);
-- // order by the only column in the selection
-- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
-- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ // order by the only column in the selection
++ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
-- // order by a column not in the selection
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ // order by a column not in the selection
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
++
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
++ }
}
}
@@@ -105,15 -104,15 +117,20 @@@
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 1, 1);
execute("INSERT INTO %s (a, b, c) VALUES (?, ?, {a: ?})", 0, 2, 2);
-- // order by a column not in the selection
-- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++ // order by a column not in the selection
++ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2));
-- assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ assertRows(execute("SELECT c.a FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c.a)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0));
++ }
dropTable("DROP TABLE %s");
}
}
@@@ -129,62 -128,62 +146,68 @@@
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 4);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 2, 5);
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0, 0, 0, 0),
-- row(0, 0, 1, 1),
-- row(0, 0, 2, 2),
-- row(0, 1, 0, 3),
-- row(0, 1, 1, 4),
-- row(0, 1, 2, 5)
-- );
--
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(0, 1, 2, 5),
-- row(0, 1, 1, 4),
-- row(0, 1, 0, 3),
-- row(0, 0, 2, 2),
-- row(0, 0, 1, 1),
-- row(0, 0, 0, 0)
-- );
--
-- assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(0, 1, 2, 5),
-- row(0, 1, 1, 4),
-- row(0, 1, 0, 3),
-- row(0, 0, 2, 2),
-- row(0, 0, 1, 1),
-- row(0, 0, 0, 0)
-- );
--
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
-- assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY d ASC", 0);
--
-- // select and order by b
-- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(0), row(0), row(1), row(1), row(1));
-- assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(1), row(1), row(1), row(0), row(0), row(0));
--
-- // select c, order by b
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2), row(0), row(1), row(2));
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0), row(2), row(1), row(0));
--
-- // select c, order by b, c
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
-- row(0), row(1), row(2), row(0), row(1), row(2));
-- assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(2), row(1), row(0), row(2), row(1), row(0));
--
-- // select d, order by b, c
-- assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
-- row(0), row(1), row(2), row(3), row(4), row(5));
-- assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(5), row(4), row(3), row(2), row(1), row(0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0, 0, 0, 0),
++ row(0, 0, 1, 1),
++ row(0, 0, 2, 2),
++ row(0, 1, 0, 3),
++ row(0, 1, 1, 4),
++ row(0, 1, 2, 5)
++ );
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(0, 1, 2, 5),
++ row(0, 1, 1, 4),
++ row(0, 1, 0, 3),
++ row(0, 0, 2, 2),
++ row(0, 0, 1, 1),
++ row(0, 0, 0, 0)
++ );
++
++ assertRows(execute("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(0, 1, 2, 5),
++ row(0, 1, 1, 4),
++ row(0, 1, 0, 3),
++ row(0, 0, 2, 2),
++ row(0, 0, 1, 1),
++ row(0, 0, 0, 0)
++ );
++
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c ASC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY c DESC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
++ assertInvalid("SELECT * FROM %s WHERE a=? ORDER BY d ASC", 0);
++
++ // select and order by b
++ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(0), row(0), row(1), row(1), row(1));
++ assertRows(execute("SELECT b FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(1), row(1), row(1), row(0), row(0), row(0));
++
++ // select c, order by b
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2), row(0), row(1), row(2));
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0), row(2), row(1), row(0));
++
++ // select c, order by b, c
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
++ row(0), row(1), row(2), row(0), row(1), row(2));
++ assertRows(execute("SELECT c FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(2), row(1), row(0), row(2), row(1), row(0));
++
++ // select d, order by b, c
++ assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
++ row(0), row(1), row(2), row(3), row(4), row(5));
++ assertRows(execute("SELECT d FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(5), row(4), row(3), row(2), row(1), row(0));
++ }
}
@Test
@@@ -198,41 -197,41 +221,46 @@@
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 4);
execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 2, 5);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c ASC", 0);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c DESC", 0);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
-- assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY d ASC", 0);
--
-- // select and order by b
-- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(0), row(0), row(1), row(1), row(1));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(1), row(1), row(1), row(0), row(0), row(0));
--
-- assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0, 0), row(0, 0), row(0, 0), row(1, 1), row(1, 1), row(1, 1));
-- assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(1, 1), row(1, 1), row(1, 1), row(0, 0), row(0, 0), row(0, 0));
--
-- // select c, order by b
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
-- row(0), row(1), row(2), row(0), row(1), row(2));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
-- row(2), row(1), row(0), row(2), row(1), row(0));
--
-- // select c, order by b, c
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
-- row(0), row(1), row(2), row(0), row(1), row(2));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(2), row(1), row(0), row(2), row(1), row(0));
--
-- // select d, order by b, c
-- assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
-- row(0), row(1), row(2), row(3), row(4), row(5));
-- assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
-- row(5), row(4), row(3), row(2), row(1), row(0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c ASC", 0);
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY c DESC", 0);
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC, c DESC", 0);
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC, c ASC", 0);
++ assertInvalid("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY d ASC", 0);
++ // select and order by b
++ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(0), row(0), row(1), row(1), row(1));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(1), row(1), row(1), row(0), row(0), row(0));
++
++ assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0, 0), row(0, 0), row(0, 0), row(1, 1), row(1, 1), row(1, 1));
++ assertRows(execute("SELECT b, blobAsInt(intAsBlob(b)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(1, 1), row(1, 1), row(1, 1), row(0, 0), row(0, 0), row(0, 0));
++
++ // select c, order by b
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC", 0),
++ row(0), row(1), row(2), row(0), row(1), row(2));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC", 0),
++ row(2), row(1), row(0), row(2), row(1), row(0));
++
++ // select c, order by b, c
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
++ row(0), row(1), row(2), row(0), row(1), row(2));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(c)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(2), row(1), row(0), row(2), row(1), row(0));
++
++ // select d, order by b, c
++ assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b ASC, c ASC", 0),
++ row(0), row(1), row(2), row(3), row(4), row(5));
++ assertRows(execute("SELECT blobAsInt(intAsBlob(d)) FROM %s WHERE a=? ORDER BY b DESC, c DESC", 0),
++ row(5), row(4), row(3), row(2), row(1), row(0));
++ }
}
/**
@@@ -247,8 -246,8 +275,14 @@@
for (int i = 0; i < 10; i++)
execute("INSERT INTO %s (k, c, v) VALUES (0, ?, ?)", i, i);
-- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC"),
-- row(9), row(8), row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC"),
++ row(9), row(8), row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
++ }
createTable("CREATE TABLE %s (k int, c1 int, c2 int, v int, PRIMARY KEY (k, c1, c2)) WITH COMPACT STORAGE");
@@@ -256,15 -255,15 +290,21 @@@
for (int j = 0; j < 2; j++)
execute("INSERT INTO %s (k, c1, c2, v) VALUES (0, ?, ?, ?)", i, j, i * 2 + j);
-- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC");
-- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c2 DESC");
-- assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY k DESC");
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c DESC");
++ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY c2 DESC");
++ assertInvalid("SELECT v FROM %s WHERE k = 0 ORDER BY k DESC");
-- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1 DESC"),
-- row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
++ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1 DESC"),
++ row(7), row(6), row(5), row(4), row(3), row(2), row(1), row(0));
-- assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1"),
-- row(0), row(1), row(2), row(3), row(4), row(5), row(6), row(7));
++ assertRows(execute("SELECT v FROM %s WHERE k = 0 ORDER BY c1"),
++ row(0), row(1), row(2), row(3), row(4), row(5), row(6), row(7));
++ }
}
/**
@@@ -281,23 -280,23 +321,29 @@@
execute("INSERT INTO %s (row, number, string) VALUES ('row', 3, 'three')");
execute("INSERT INTO %s (row, number, string) VALUES ('row', 4, 'four')");
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number ASC"),
-- row(1), row(2));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number ASC"),
++ row(1), row(2));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number ASC"),
-- row(3), row(4));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number ASC"),
++ row(3), row(4));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number DESC"),
-- row(2), row(1));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number < 3 ORDER BY number DESC"),
++ row(2), row(1));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number DESC"),
-- row(4), row(3));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number >= 3 ORDER BY number DESC"),
++ row(4), row(3));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number > 3 ORDER BY number DESC"),
-- row(4));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number > 3 ORDER BY number DESC"),
++ row(4));
-- assertRows(execute("SELECT number FROM %s WHERE row='row' AND number <= 3 ORDER BY number DESC"),
-- row(3), row(2), row(1));
++ assertRows(execute("SELECT number FROM %s WHERE row='row' AND number <= 3 ORDER BY number DESC"),
++ row(3), row(2), row(1));
++ }
}
/**
@@@ -330,14 -329,14 +376,20 @@@
execute("INSERT INTO %s (my_id, col1, value) VALUES ( 'key3', 2, 'b')");
execute("INSERT INTO %s (my_id, col1, value) VALUES ( 'key4', 4, 'd')");
-- assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
-- row(1), row(2), row(3));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
+
- assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row(1, "key1"), row(2, "key3"), row(3, "key2"));
++ assertRows(execute("SELECT col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
++ row(1), row(2), row(3));
- assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row("key1", 1), row("key3", 2), row("key2", 3));
- assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row(1, "key1"), row(2, "key3"), row(3, "key2"));
++ assertRows(execute("SELECT col1, my_id FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
++ row(1, "key1"), row(2, "key3"), row(3, "key2"));
+
- assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
- row("key1", 1), row("key3", 2), row("key2", 3));
++ assertRows(execute("SELECT my_id, col1 FROM %s WHERE my_id in('key1', 'key2', 'key3') ORDER BY col1"),
++ row("key1", 1), row("key3", 2), row("key2", 3));
++ }
createTable("CREATE TABLE %s (pk1 int, pk2 int, c int, v text, PRIMARY KEY ((pk1, pk2), c) )");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 2, "A");
@@@ -345,25 -344,25 +397,31 @@@
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c, v) VALUES (?, ?, ?, ?)", 1, 1, 4, "D");
-- assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
-- row("B", null, 1),
-- row("A", null, 2),
-- row("D", null, 4));
--
-- assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
-- row("B", null, 1),
-- row("A", null, 2),
-- row("D", null, 4));
--
-- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
-- row("B"),
-- row("A"),
-- row("D"));
--
-- assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
-- row("B"),
-- row("A"),
-- row("D"));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT v, ttl(v), c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B", null, 1),
++ row("A", null, 2),
++ row("D", null, 4));
++
++ assertRows(execute("SELECT v, ttl(v), c as name_1 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B", null, 1),
++ row("A", null, 2),
++ row("D", null, 4));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
++
++ assertRows(execute("SELECT v as c FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c; ", 1, 1, 2),
++ row("B"),
++ row("A"),
++ row("D"));
++ }
createTable("CREATE TABLE %s (pk1 int, pk2 int, c1 int, c2 int, v text, PRIMARY KEY ((pk1, pk2), c1, c2) )");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 4, "A");
@@@ -371,68 -370,25 +429,80 @@@
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 3, 3, 3, "C");
execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 4, 1, "D");
-- assertRows(execute("SELECT v, ttl(v), c1, c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
-- row("B", null, 1, 2),
-- row("D", null, 4, 1),
-- row("A", null, 4, 4));
--
-- assertRows(execute("SELECT v, ttl(v), c1 as name_1, c2 as name_2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
-- row("B", null, 1, 2),
-- row("D", null, 4, 1),
-- row("A", null, 4, 4));
--
-- assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
-- row("B"),
-- row("D"),
-- row("A"));
--
-- assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
-- row("B"),
-- row("D"),
-- row("A"));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT v, ttl(v), c1, c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
++ row("B", null, 1, 2),
++ row("D", null, 4, 1),
++ row("A", null, 4, 4));
++
++ assertRows(execute("SELECT v, ttl(v), c1 as name_1, c2 as name_2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
++ row("B", null, 1, 2),
++ row("D", null, 4, 1),
++ row("A", null, 4, 4));
++
++ assertRows(execute("SELECT v FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
++ row("B"),
++ row("D"),
++ row("A"));
++
++ assertRows(execute("SELECT v as c2 FROM %s where pk1 = ? AND pk2 IN (?, ?) ORDER BY c1, c2; ", 1, 1, 2),
++ row("B"),
++ row("D"),
++ row("A"));
++ }
+ }
+
+ @Test
+ public void testOrderByForInClauseWithNullValue() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int, s int static, d int, PRIMARY KEY (a, b, c))");
+
+ execute("INSERT INTO %s (a, b, c, d) VALUES (1, 1, 1, 1)");
+ execute("INSERT INTO %s (a, b, c, d) VALUES (1, 1, 2, 1)");
+ execute("INSERT INTO %s (a, b, c, d) VALUES (2, 2, 1, 1)");
+ execute("INSERT INTO %s (a, b, c, d) VALUES (2, 2, 2, 1)");
+
+ execute("UPDATE %s SET s = 1 WHERE a = 1");
+ execute("UPDATE %s SET s = 2 WHERE a = 2");
+ execute("UPDATE %s SET s = 3 WHERE a = 3");
+
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC"),
- row(2, 2, 2, 1, 2),
- row(2, 2, 1, 1, 2),
- row(1, 1, 2, 1, 1),
- row(1, 1, 1, 1, 1),
- row(3, null, null, null, 3));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC"),
- row(3, null, null, null, 3),
- row(1, 1, 1, 1, 1),
- row(1, 1, 2, 1, 1),
- row(2, 2, 1, 1, 2),
- row(2, 2, 2, 1, 2));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC , c DESC"),
- row(2, 2, 2, 1, 2),
- row(2, 2, 1, 1, 2),
- row(1, 1, 2, 1, 1),
- row(1, 1, 1, 1, 1),
- row(3, null, null, null, 3));
-
- assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC, c ASC"),
- row(3, null, null, null, 3),
- row(1, 1, 1, 1, 1),
- row(1, 1, 2, 1, 1),
- row(2, 2, 1, 1, 2),
- row(2, 2, 2, 1, 2));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC"),
++ row(2, 2, 2, 1, 2),
++ row(2, 2, 1, 1, 2),
++ row(1, 1, 2, 1, 1),
++ row(1, 1, 1, 1, 1),
++ row(3, null, null, null, 3));
++
++ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC"),
++ row(3, null, null, null, 3),
++ row(1, 1, 1, 1, 1),
++ row(1, 1, 2, 1, 1),
++ row(2, 2, 1, 1, 2),
++ row(2, 2, 2, 1, 2));
++
++ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b DESC , c DESC"),
++ row(2, 2, 2, 1, 2),
++ row(2, 2, 1, 1, 2),
++ row(1, 1, 2, 1, 1),
++ row(1, 1, 1, 1, 1),
++ row(3, null, null, null, 3));
++
++ assertRows(execute("SELECT a, b, c, d, s FROM %s WHERE a IN (1, 2, 3) ORDER BY b ASC, c ASC"),
++ row(3, null, null, null, 3),
++ row(1, 1, 1, 1, 1),
++ row(1, 1, 2, 1, 1),
++ row(2, 2, 1, 1, 2),
++ row(2, 2, 2, 1, 2));
++ }
}
/**
@@@ -447,13 -403,13 +517,19 @@@
for(int i =0; i < 10; i++)
execute("INSERT INTO %s (k, c, v) VALUES (0, ?, ?)", i, i);
-- assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c ASC"),
-- row(0, 0), row(1, 1), row(2, 2), row(3, 3), row(4, 4),
-- row(5, 5), row(6, 6), row(7, 7), row(8, 8), row(9, 9));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c ASC"),
++ row(0, 0), row(1, 1), row(2, 2), row(3, 3), row(4, 4),
++ row(5, 5), row(6, 6), row(7, 7), row(8, 8), row(9, 9));
-- assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c DESC"),
-- row(9, 9), row(8, 8), row(7, 7), row(6, 6), row(5, 5),
-- row(4, 4), row(3, 3), row(2, 2), row(1, 1), row(0, 0));
++ assertRows(execute("SELECT c, v FROM %s WHERE k = 0 ORDER BY c DESC"),
++ row(9, 9), row(8, 8), row(7, 7), row(6, 6), row(5, 5),
++ row(4, 4), row(3, 3), row(2, 2), row(1, 1), row(0, 0));
++ }
createTable("CREATE TABLE %s (k int, c1 int, c2 int, v text, PRIMARY KEY (k, c1, c2)) WITH CLUSTERING ORDER BY (c1 ASC, c2 DESC)");
@@@ -461,28 -417,28 +537,34 @@@
for(int j = 0; j < 10; j++)
execute("INSERT INTO %s (k, c1, c2, v) VALUES (0, ?, ?, ?)", i, j, String.format("%d%d", i, j));
-- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 ASC");
-- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 DESC");
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
-- Object[][] expectedRows = new Object[100][];
-- for(int i = 0; i < 10; i++)
-- for(int j = 9; j >= 0; j--)
-- expectedRows[i * 10 + (9 - j)] = row(i, j, String.format("%d%d", i, j));
++ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 ASC");
++ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 DESC");
-- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC"),
-- expectedRows);
++ Object[][] expectedRows = new Object[100][];
++ for(int i = 0; i < 10; i++)
++ for(int j = 9; j >= 0; j--)
++ expectedRows[i * 10 + (9 - j)] = row(i, j, String.format("%d%d", i, j));
-- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC"),
-- expectedRows);
++ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC"),
++ expectedRows);
-- for(int i = 9; i >= 0; i--)
-- for(int j = 0; j < 10; j++)
-- expectedRows[(9 - i) * 10 + j] = row(i, j, String.format("%d%d", i, j));
++ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 ASC, c2 DESC"),
++ expectedRows);
++
++ for(int i = 9; i >= 0; i--)
++ for(int j = 0; j < 10; j++)
++ expectedRows[(9 - i) * 10 + j] = row(i, j, String.format("%d%d", i, j));
-- assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC"),
-- expectedRows);
++ assertRows(execute("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c1 DESC, c2 ASC"),
++ expectedRows);
-- assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c2 DESC, c1 ASC");
++ assertInvalid("SELECT c1, c2, v FROM %s WHERE k = 0 ORDER BY c2 DESC, c1 ASC");
++ }
}
/**
@@@ -497,18 -453,18 +579,24 @@@
for (int j = 0; j < 2; j++)
execute("INSERT INTO %s (k, c1, c2) VALUES ('foo', ?, ?)", i, j);
-- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
-- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo'"),
++ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
-- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
-- row(0, 1), row(0, 0), row(1, 1), row(1, 0));
++ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 DESC"),
++ row(0, 1), row(0, 0), row(1, 1), row(1, 0));
-- assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
-- row(1, 0), row(1, 1), row(0, 0), row(0, 1));
++ assertRows(execute("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 DESC, c2 ASC"),
++ row(1, 0), row(1, 1), row(0, 0), row(0, 1));
-- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
-- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
-- assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
++ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 DESC");
++ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c2 ASC");
++ assertInvalid("SELECT c1, c2 FROM %s WHERE k = 'foo' ORDER BY c1 ASC, c2 ASC");
++ }
}
/**
@@@ -522,9 -478,9 +610,15 @@@
execute("INSERT INTO %s(k, c1, c2) VALUES (0, 0, 1)");
execute("INSERT INTO %s(k, c1, c2) VALUES (0, 0, 2)");
-- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
-- row(0, 0, 2),
-- row(0, 0, 0));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
++ row(0, 0, 2),
++ row(0, 0, 0));
++ }
}
/**
@@@ -543,87 -499,44 +637,99 @@@
execute("INSERT INTO %s (k, c1, c2, v) VALUES (1, 1, 1, 4)");
execute("INSERT INTO %s (k, c1, c2, v) VALUES (1, 1, 2, 5)");
-- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
-- row(0, 0, 0, 0),
-- row(0, 0, 2, 2));
-- assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC, c2 ASC"),
-- row(0, 0, 0, 0),
-- row(0, 0, 2, 2));
--
-- // check that we don 't need to select the column on which we order
-- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
-- row(0),
-- row(2));
-- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC"),
-- row(0),
-- row(2));
-- assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
-- row(2),
-- row(0));
-
-- assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0)"),
- row(0),
- row(1),
- row(2),
-- row(3),
-- row(4),
- row(5));
- row(5),
- row(0),
- row(1),
- row(2));
--
-- assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"),
-- row(0),
-- row(1),
-- row(2),
-- row(3),
-- row(4),
-- row(5));
--
-- // we should also be able to use functions in the select clause (additional test for CASSANDRA - 8286)
-- Object[][] results = getRows(execute("SELECT writetime(v) FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"));
--
-- // since we don 't know the write times, just assert that the order matches the order we expect
-- assertTrue(isFirstIntSorted(results));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
++ row(0, 0, 0, 0),
++ row(0, 0, 2, 2));
++ assertRows(execute("SELECT * FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC, c2 ASC"),
++ row(0, 0, 0, 0),
++ row(0, 0, 2, 2));
++
++ // check that we don 't need to select the column on which we order
++ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0)"),
++ row(0),
++ row(2));
++ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 ASC"),
++ row(0),
++ row(2));
++ assertRows(execute("SELECT v FROM %s WHERE k=0 AND c1 = 0 AND c2 IN (2, 0) ORDER BY c1 DESC"),
++ row(2),
++ row(0));
++
++ assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0)"),
++ row(0),
++ row(1),
++ row(2),
++ row(3),
++ row(4),
++ row(5));
++
++ assertRows(execute("SELECT v FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"),
++ row(0),
++ row(1),
++ row(2),
++ row(3),
++ row(4),
++ row(5));
++
++ // we should also be able to use functions in the select clause (additional test for CASSANDRA - 8286)
++ Object[][] results = getRows(execute("SELECT writetime(v) FROM %s WHERE k IN (1, 0) ORDER BY c1 ASC"));
++
++ // since we don 't know the write times, just assert that the order matches the order we expect
++ assertTrue(isFirstIntSorted(results));
++ }
+ }
+
+ @Test
+ public void testInOrderByWithTwoPartitionKeyColumns() throws Throwable
+ {
+ for (String option : asList("", "WITH CLUSTERING ORDER BY (col_3 DESC)"))
+ {
+ createTable("CREATE TABLE %s (col_1 int, col_2 int, col_3 int, PRIMARY KEY ((col_1, col_2), col_3)) " + option);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 1, 1);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 1, 2);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 1, 13);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 2, 10);
+ execute("INSERT INTO %s (col_1, col_2, col_3) VALUES(?, ?, ?)", 1, 2, 11);
+
- assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3;", 1, 1, 2),
- row(1, 1, 1),
- row(1, 1, 2),
- row(1, 2, 10),
- row(1, 2, 11),
- row(1, 1, 13));
-
- assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3 desc;", 1, 1, 2),
- row(1, 1, 13),
- row(1, 2, 11),
- row(1, 2, 10),
- row(1, 1, 2),
- row(1, 1, 1));
-
- assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3;", 1, 2, 1),
- row(1, 1, 1),
- row(1, 1, 2),
- row(1, 2, 10),
- row(1, 2, 11),
- row(1, 1, 13));
-
- assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3 desc;", 1, 2, 1),
- row(1, 1, 13),
- row(1, 2, 11),
- row(1, 2, 10),
- row(1, 1, 2),
- row(1, 1, 1));
++ for (boolean flush : new boolean[]{true, false})
++ {
++ if (flush)
++ flush();
++
++ assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3;", 1, 1, 2),
++ row(1, 1, 1),
++ row(1, 1, 2),
++ row(1, 2, 10),
++ row(1, 2, 11),
++ row(1, 1, 13));
++
++ assertRows(execute("select * from %s where col_1=? and col_2 IN (?, ?) order by col_3 desc;", 1, 1, 2),
++ row(1, 1, 13),
++ row(1, 2, 11),
++ row(1, 2, 10),
++ row(1, 1, 2),
++ row(1, 1, 1));
++
++ assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3;", 1, 2, 1),
++ row(1, 1, 1),
++ row(1, 1, 2),
++ row(1, 2, 10),
++ row(1, 2, 11),
++ row(1, 1, 13));
++
++ assertRows(execute("select * from %s where col_2 IN (?, ?) and col_1=? order by col_3 desc;", 1, 2, 1),
++ row(1, 1, 13),
++ row(1, 2, 11),
++ row(1, 2, 10),
++ row(1, 1, 2),
++ row(1, 1, 1));
++ }
+ }
}
private boolean isFirstIntSorted(Object[][] rows)
[12/12] cassandra git commit: Merge branch cassandra-3.9 into trunk
Posted by bl...@apache.org.
Merge branch cassandra-3.9 into trunk
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/d1bd71c4
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d1bd71c4
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d1bd71c4
Branch: refs/heads/trunk
Commit: d1bd71c4ea5e02d53bfcd99d5fb390d3ee18d17c
Parents: e22170e cb97649
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 16 15:50:14 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 16 15:52:20 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 11 +-
.../columniterator/AbstractSSTableIterator.java | 24 +-
.../db/columniterator/SSTableIterator.java | 17 +
.../columniterator/SSTableReversedIterator.java | 19 +-
.../cassandra/db/compaction/Scrubber.java | 170 ++++-
.../cassandra/db/marshal/ReversedType.java | 10 -
.../db/partitions/AbstractBTreePartition.java | 7 +-
.../db/partitions/ImmutableBTreePartition.java | 34 +-
.../db/rows/UnfilteredRowIterators.java | 28 +
.../validation/entities/SecondaryIndexTest.java | 131 ++++
.../cql3/validation/operations/DeleteTest.java | 113 +++
.../operations/SelectOrderByTest.java | 765 ++++++++++---------
.../cql3/validation/operations/SelectTest.java | 355 ++++++++-
.../cassandra/db/marshal/ReversedTypeTest.java | 4 +-
15 files changed, 1269 insertions(+), 420 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d1bd71c4/CHANGES.txt
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d1bd71c4/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index 56d2827,2edff8e..9bdeec1
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -13,64 -13,16 +13,73 @@@ restore snapshots created with the prev
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
-
+3.10
+====
+
+New features
+------------
+ - Support for GROUP BY queries has been added.
+ - A new compaction-stress tool has been added to test the throughput of compaction
+ for any cassandra-stress user schema. see compaction-stress help for how to use.
+ - Compaction can now take into account overlapping tables that don't take part
+ in the compaction to look for deleted or overwritten data in the compacted tables.
+ Then such data is found, it can be safely discarded, which in turn should enable
+ the removal of tombstones over that data.
+
+ The behavior can be engaged in two ways:
+ - as a "nodetool garbagecollect -g CELL/ROW" operation, which applies
+ single-table compaction on all sstables to discard deleted data in one step.
+ - as a "provide_overlapping_tombstones:CELL/ROW/NONE" compaction strategy flag,
+ which uses overlapping tables as a source of deletions/overwrites during all
+ compactions.
+ The argument specifies the granularity at which deleted data is to be found:
+ - If ROW is specified, only whole deleted rows (or sets of rows) will be
+ discarded.
+ - If CELL is specified, any columns whose value is overwritten or deleted
+ will also be discarded.
+ - NONE (default) specifies the old behavior, overlapping tables are not used to
+ decide when to discard data.
+ Which option to use depends on your workload, both ROW and CELL increase the
+ disk load on compaction (especially with the size-tiered compaction strategy),
+ with CELL being more resource-intensive. Both should lead to better read
+ performance if deleting rows (resp. overwriting or deleting cells) is common.
+ - Prepared statements are now persisted in the table prepared_statements in
+ the system keyspace. Upon startup, this table is used to preload all
+ previously prepared statements - i.e. in many cases clients do not need to
+ re-prepare statements against restarted nodes.
+ - cqlsh can now connect to older Cassandra versions by downgrading the native
+ protocol version. Please note that this is currently not part of our release
+ testing and, as a consequence, it is not guaranteed to work in all cases.
+ See CASSANDRA-12150 for more details.
+ - Snapshots that are automatically taken before a table is dropped or truncated
+ will have a "dropped" or "truncated" prefix on their snapshot tag name.
+ - Metrics are exposed for successful and failed authentication attempts.
+ These can be located using the object names org.apache.cassandra.metrics:type=Client,name=AuthSuccess
+ and org.apache.cassandra.metrics:type=Client,name=AuthFailure respectively.
+ - Add support to "unset" JSON fields in prepared statements by specifying DEFAULT UNSET.
+ See CASSANDRA-11424 for details
+
+Upgrading
+---------
+ - Logs written to stdout are now consistent with logs written to files.
+ Time is now local (it was UTC on the console and local in files). Date, thread, file
+ and line info where added to stdout. (see CASSANDRA-12004)
+ - The 'clientutil' jar, which has been somewhat broken on the 3.x branch, is not longer provided.
+ The features provided by that jar are provided by any good java driver and we advise relying on drivers rather on
+ that jar, but if you need that jar for backward compatiblity until you do so, you should use the version provided
+ on previous Cassandra branch, like the 3.0 branch (by design, the functionality provided by that jar are stable
+ accross versions so using the 3.0 jar for a client connecting to 3.x should work without issues).
+
+ 3.9
+ ===
+
+ Upgrading
+ ---------
+ - The ReversedType behaviour has been corrected for clustering columns of
+ BYTES type containing empty value. Scrub should be run on the existing
+ SSTables containing a descending clustering column of BYTES type to correct
+ their ordering. See CASSANDRA-12127 for more details.
+
3.8
===
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d1bd71c4/src/java/org/apache/cassandra/db/columniterator/AbstractSSTableIterator.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d1bd71c4/src/java/org/apache/cassandra/db/columniterator/SSTableIterator.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d1bd71c4/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d1bd71c4/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/compaction/Scrubber.java
index 2cfc75d,5443d8e..0007e30
--- a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
+++ b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
@@@ -325,6 -299,38 +299,38 @@@ public class Scrubber implements Closea
}
}
+ @SuppressWarnings("resource")
+ private boolean tryAppend(DecoratedKey prevKey, DecoratedKey key, SSTableRewriter writer)
+ {
+ // OrderCheckerIterator will check, at iteration time, that the rows are in the proper order. If it detects
+ // that one row is out of order, it will stop returning them. The remaining rows will be sorted and added
+ // to the outOfOrder set that will be later written to a new SSTable.
- OrderCheckerIterator sstableIterator = new OrderCheckerIterator(new RowMergingSSTableIterator(sstable, dataFile, key),
++ OrderCheckerIterator sstableIterator = new OrderCheckerIterator(new RowMergingSSTableIterator(SSTableIdentityIterator.create(sstable, dataFile, key)),
+ cfs.metadata.comparator);
+
+ try (UnfilteredRowIterator iterator = withValidation(sstableIterator, dataFile.getPath()))
+ {
+ if (prevKey != null && prevKey.compareTo(key) > 0)
+ {
+ saveOutOfOrderRow(prevKey, key, iterator);
+ return false;
+ }
+
+ if (writer.tryAppend(iterator) == null)
+ emptyRows++;
+ else
+ goodRows++;
+ }
+
+ if (sstableIterator.hasRowsOutOfOrder())
+ {
+ outputHandler.warn(String.format("Out of order rows found in partition: %s", key));
+ outOfOrder.add(sstableIterator.getRowsOutOfOrder());
+ }
+
+ return true;
+ }
+
private void updateIndexKey()
{
currentIndexKey = nextIndexKey;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d1bd71c4/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------
[09/12] cassandra git commit: Merge branch cassandra-2.2 into
cassandra-3.0
Posted by bl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
index 814e822,6bd5f26..b9ccafb
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
@@@ -26,8 -28,10 +28,11 @@@ import org.apache.commons.lang3.StringU
import org.junit.Test;
import org.apache.cassandra.cql3.CQLTester;
+
-import static org.junit.Assert.assertEquals;
+ import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+import static org.apache.commons.lang3.StringUtils.isEmpty;
+import static org.junit.Assert.assertEquals;
public class DeleteTest extends CQLTester
{
@@@ -326,761 -330,42 +331,869 @@@
assertEmpty(execute("select * from %s where a=1 and b=1"));
}
+ @Test
+ public void testDeleteWithNoClusteringColumns() throws Throwable
+ {
+ testDeleteWithNoClusteringColumns(false);
+ testDeleteWithNoClusteringColumns(true);
+ }
+
+ private void testDeleteWithNoClusteringColumns(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] {"", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int PRIMARY KEY," +
+ "value int)" + compactOption);
+
+ execute("INSERT INTO %s (partitionKey, value) VALUES (0, 0)");
+ execute("INSERT INTO %s (partitionKey, value) VALUES (1, 1)");
+ execute("INSERT INTO %s (partitionKey, value) VALUES (2, 2)");
+ execute("INSERT INTO %s (partitionKey, value) VALUES (3, 3)");
+ flush(forceFlush);
+
+ execute("DELETE value FROM %s WHERE partitionKey = ?", 0);
+ flush(forceFlush);
+
+ if (isEmpty(compactOption))
+ {
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, null));
+ }
+ else
+ {
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ?", 0));
+ }
+
+ execute("DELETE FROM %s WHERE partitionKey IN (?, ?)", 0, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s"),
+ row(2, 2),
+ row(3, 3));
+
+ // test invalid queries
+
+ // token function
+ assertInvalidMessage("The token function cannot be used in WHERE clauses for DELETE statements",
+ "DELETE FROM %s WHERE token(partitionKey) = token(?)", 0);
+
+ // multiple time same primary key element in WHERE clause
+ assertInvalidMessage("partitionkey cannot be restricted by more than one relation if it includes an Equal",
+ "DELETE FROM %s WHERE partitionKey = ? AND partitionKey = ?", 0, 1);
+
+ // unknown identifiers
+ assertInvalidMessage("Unknown identifier unknown",
+ "DELETE unknown FROM %s WHERE partitionKey = ?", 0);
+
+ assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
+ "DELETE FROM %s WHERE partitionKey1 = ?", 0);
+
+ // Invalid operator in the where clause
+ assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
+ "DELETE FROM %s WHERE partitionKey > ? ", 0);
+
+ assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
+ "DELETE FROM %s WHERE partitionKey CONTAINS ?", 0);
+
+ // Non primary key in the where clause
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND value = ?", 0, 1);
+ }
+ }
+
+ @Test
+ public void testDeleteWithOneClusteringColumns() throws Throwable
+ {
+ testDeleteWithOneClusteringColumns(false);
+ testDeleteWithOneClusteringColumns(true);
+ }
+
+ private void testDeleteWithOneClusteringColumns(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] {"", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering int," +
+ "value int," +
+ " PRIMARY KEY (partitionKey, clustering))" + compactOption);
+
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 0, 0)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 1, 1)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 2, 2)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 3, 3)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 4, 4)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 5, 5)");
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (1, 0, 6)");
+ flush(forceFlush);
+
+ execute("DELETE value FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1);
+ flush(forceFlush);
+ if (isEmpty(compactOption))
+ {
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1),
+ row(0, 1, null));
+ }
+ else
+ {
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1));
+ }
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) = (?)", 0, 1);
+ flush(forceFlush);
+ assertEmpty(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1));
+
+ execute("DELETE FROM %s WHERE partitionKey IN (?, ?) AND clustering = ?", 0, 1, 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(0, 2, 2),
+ row(0, 3, 3),
+ row(0, 4, 4),
+ row(0, 5, 5));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) IN ((?), (?))", 0, 4, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(0, 2, 2),
+ row(0, 3, 3));
+
+ // test invalid queries
+
+ // missing primary key element
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE clustering = ?", 1);
+
+ // token function
+ assertInvalidMessage("The token function cannot be used in WHERE clauses for DELETE statements",
+ "DELETE FROM %s WHERE token(partitionKey) = token(?) AND clustering = ? ", 0, 1);
+
+ // multiple time same primary key element in WHERE clause
+ assertInvalidMessage("clustering cannot be restricted by more than one relation if it includes an Equal",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering = ? AND clustering = ?", 0, 1, 1);
+
+ // unknown identifiers
+ assertInvalidMessage("Unknown identifier value1",
+ "DELETE value1 FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1);
+
+ assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
+ "DELETE FROM %s WHERE partitionKey1 = ? AND clustering = ?", 0, 1);
+
+ assertInvalidMessage("Undefined name clustering_3 in where clause ('clustering_3 = ?')",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_3 = ?", 0, 1);
+
+ // Invalid operator in the where clause
+ assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
+ "DELETE FROM %s WHERE partitionKey > ? AND clustering = ?", 0, 1);
+
+ assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
+ "DELETE FROM %s WHERE partitionKey CONTAINS ? AND clustering = ?", 0, 1);
+
+ // Non primary key in the where clause
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering = ? AND value = ?", 0, 1, 3);
+ }
+ }
+
+ @Test
+ public void testDeleteWithTwoClusteringColumns() throws Throwable
+ {
+ testDeleteWithTwoClusteringColumns(false);
+ testDeleteWithTwoClusteringColumns(true);
+ }
+
+ private void testDeleteWithTwoClusteringColumns(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering_1 int," +
+ "clustering_2 int," +
+ "value int," +
+ " PRIMARY KEY (partitionKey, clustering_1, clustering_2))" + compactOption);
+
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 0, 0)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 2, 2)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 3, 3)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 1, 1, 4)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 1, 2, 5)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (1, 0, 0, 6)");
+ flush(forceFlush);
+
+ execute("DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+ flush(forceFlush);
+
+ if (isEmpty(compactOption))
+ {
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 0, 1, 1),
+ row(0, 1, 1, null));
+ }
+ else
+ {
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 0, 1, 1));
+ }
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) = (?, ?)", 0, 1, 1);
+ flush(forceFlush);
+ assertEmpty(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 0, 1, 1));
+
+ execute("DELETE FROM %s WHERE partitionKey IN (?, ?) AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 0, 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(0, 0, 1, 1),
+ row(0, 0, 2, 2),
+ row(0, 0, 3, 3),
+ row(0, 1, 2, 5));
+
+ Object[][] rows;
+ if (isEmpty(compactOption))
+ {
+ rows = new Object[][]{row(0, 0, 1, 1),
+ row(0, 0, 2, null),
+ row(0, 0, 3, null),
+ row(0, 1, 2, 5)};
+ }
+ else
+ {
+ rows = new Object[][]{row(0, 0, 1, 1), row(0, 1, 2, 5)};
+ }
+
+ execute("DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 IN (?, ?)", 0, 0, 2, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1), rows);
+
+ if (isEmpty(compactOption))
+ {
+ rows = new Object[][]{row(0, 0, 1, 1),
+ row(0, 0, 3, null)};
+ }
+ else
+ {
+ rows = new Object[][]{row(0, 0, 1, 1)};
+ }
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) IN ((?, ?), (?, ?))", 0, 0, 2, 1, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1), rows);
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1) IN ((?), (?)) AND clustering_2 = ?", 0, 0, 2, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(0, 0, 1, 1));
+
+ // test invalid queries
+
+ // missing primary key element
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE clustering_1 = ? AND clustering_2 = ?", 1, 1);
+
+ assertInvalidMessage("PRIMARY KEY column \"clustering_2\" cannot be restricted as preceding column \"clustering_1\" is not restricted",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_2 = ?", 0, 1);
+
+ // token function
+ assertInvalidMessage("The token function cannot be used in WHERE clauses for DELETE statements",
+ "DELETE FROM %s WHERE token(partitionKey) = token(?) AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ // multiple time same primary key element in WHERE clause
+ assertInvalidMessage("clustering_1 cannot be restricted by more than one relation if it includes an Equal",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ? AND clustering_1 = ?", 0, 1, 1, 1);
+
+ // unknown identifiers
+ assertInvalidMessage("Unknown identifier value1",
+ "DELETE value1 FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
+ "DELETE FROM %s WHERE partitionKey1 = ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ assertInvalidMessage("Undefined name clustering_3 in where clause ('clustering_3 = ?')",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_3 = ?", 0, 1, 1);
+
+ // Invalid operator in the where clause
+ assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
+ "DELETE FROM %s WHERE partitionKey > ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
+ "DELETE FROM %s WHERE partitionKey CONTAINS ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
+
+ // Non primary key in the where clause
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ? AND value = ?", 0, 1, 1, 3);
+ }
+ }
+
+ @Test
+ public void testDeleteWithRangeAndOneClusteringColumn() throws Throwable
+ {
+ testDeleteWithRangeAndOneClusteringColumn(false);
+ testDeleteWithRangeAndOneClusteringColumn(true);
+ }
+
+ private void testDeleteWithRangeAndOneClusteringColumn(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering int," +
+ "value int," +
+ " PRIMARY KEY (partitionKey, clustering))" + compactOption);
+
+ int value = 0;
+ for (int partitionKey = 0; partitionKey < 5; partitionKey++)
+ for (int clustering1 = 0; clustering1 < 5; clustering1++)
+ execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (?, ?, ?)",
+ partitionKey, clustering1, value++);
+
+ flush(forceFlush);
+
+ // test delete partition
+ execute("DELETE FROM %s WHERE partitionKey = ?", 1);
+ flush(forceFlush);
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ?", 1));
+
+ // test slices on the first clustering column
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering >= ?", 0, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 0, 0),
+ row(0, 1, 1),
+ row(0, 2, 2),
+ row(0, 3, 3));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering > ?", 0, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 0, 0),
+ row(0, 1, 1),
+ row(0, 2, 2));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering <= ?", 0, 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 1, 1),
+ row(0, 2, 2));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering < ?", 0, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 2, 2));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering >= ? AND clustering < ?", 2, 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 13),
+ row(2, 4, 14));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering > ? AND clustering <= ?", 2, 3, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 13));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering < ? AND clustering > ?", 2, 3, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 13));
+
+ // test multi-column slices
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) > (?)", 3, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
+ row(3, 0, 15),
+ row(3, 1, 16),
+ row(3, 2, 17));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) < (?)", 3, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
+ row(3, 1, 16),
+ row(3, 2, 17));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) >= (?) AND (clustering) <= (?)", 3, 0, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
+ row(3, 2, 17));
+
+ // Test invalid queries
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ? AND clustering >= ?", 2, 1);
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ?", 2);
+ }
+ }
+
+ @Test
+ public void testDeleteWithRangeAndTwoClusteringColumns() throws Throwable
+ {
+ testDeleteWithRangeAndTwoClusteringColumns(false);
+ testDeleteWithRangeAndTwoClusteringColumns(true);
+ }
+
+ private void testDeleteWithRangeAndTwoClusteringColumns(boolean forceFlush) throws Throwable
+ {
+ for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering_1 int," +
+ "clustering_2 int," +
+ "value int," +
+ " PRIMARY KEY (partitionKey, clustering_1, clustering_2))" + compactOption);
+
+ int value = 0;
+ for (int partitionKey = 0; partitionKey < 5; partitionKey++)
+ for (int clustering1 = 0; clustering1 < 5; clustering1++)
+ for (int clustering2 = 0; clustering2 < 5; clustering2++) {
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (?, ?, ?, ?)",
+ partitionKey, clustering1, clustering2, value++);}
+ flush(forceFlush);
+
+ // test unspecified second clustering column
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 2),
+ row(0, 0, 0, 0),
+ row(0, 0, 1, 1),
+ row(0, 0, 2, 2),
+ row(0, 0, 3, 3),
+ row(0, 0, 4, 4));
+
+ // test delete partition
+ execute("DELETE FROM %s WHERE partitionKey = ?", 1);
+ flush(forceFlush);
+ assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ?", 1));
+
+ // test slices on the second clustering column
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 < ?", 0, 0, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 2),
+ row(0, 0, 2, 2),
+ row(0, 0, 3, 3),
+ row(0, 0, 4, 4));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 <= ?", 0, 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 2),
+ row(0, 0, 4, 4));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 > ? ", 0, 2, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 2),
+ row(0, 2, 0, 10),
+ row(0, 2, 1, 11),
+ row(0, 2, 2, 12));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 >= ? ", 0, 2, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 2),
+ row(0, 2, 0, 10));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 > ? AND clustering_2 < ? ",
+ 0, 3, 1, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 3),
+ row(0, 3, 0, 15),
+ row(0, 3, 1, 16),
+ row(0, 3, 4, 19));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 > ? AND clustering_2 < ? ",
+ 0, 3, 4, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 3),
+ row(0, 3, 0, 15),
+ row(0, 3, 1, 16),
+ row(0, 3, 4, 19));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 >= ? AND clustering_2 <= ? ",
+ 0, 3, 1, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 3),
+ row(0, 3, 0, 15));
+
+ // test slices on the first clustering column
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 >= ?", 0, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 0, 4, 4),
+ row(0, 2, 0, 10),
+ row(0, 3, 0, 15));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 > ?", 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 0, 4, 4),
+ row(0, 2, 0, 10),
+ row(0, 3, 0, 15));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
+ row(0, 3, 0, 15));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 >= ? AND clustering_1 < ?", 2, 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 0, 65),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68),
+ row(2, 3, 4, 69),
+ row(2, 4, 0, 70),
+ row(2, 4, 1, 71),
+ row(2, 4, 2, 72),
+ row(2, 4, 3, 73),
+ row(2, 4, 4, 74));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 > ? AND clustering_1 <= ?", 2, 3, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 0, 65),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68),
+ row(2, 3, 4, 69));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 < ? AND clustering_1 > ?", 2, 3, 5);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 0, 65),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68),
+ row(2, 3, 4, 69));
+
+ // test multi-column slices
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) > (?, ?)", 2, 3, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 0, 65),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) < (?, ?)", 2, 3, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 1, 66),
+ row(2, 3, 2, 67),
+ row(2, 3, 3, 68));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) >= (?, ?) AND (clustering_1) <= (?)", 2, 3, 2, 4);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+ row(2, 3, 1, 66));
+
+ // Test with a mix of single column and multi-column restrictions
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND (clustering_2) < (?)", 3, 0, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 3, 0),
+ row(3, 0, 3, 78),
+ row(3, 0, 4, 79));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?) AND (clustering_2) >= (?)", 3, 0, 1, 3);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 3, 0, 1),
+ row(3, 1, 0, 80),
+ row(3, 1, 1, 81),
+ row(3, 1, 2, 82));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1) IN ((?), (?)) AND clustering_2 < ?", 3, 0, 1, 1);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 3, 0, 1),
+ row(3, 1, 1, 81),
+ row(3, 1, 2, 82));
+
+ execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1) = (?) AND clustering_2 >= ?", 3, 1, 2);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 3, 0, 1),
+ row(3, 1, 1, 81));
+
+ // Test invalid queries
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) >= (?, ?)", 2, 3, 1);
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 >= ?", 2, 3);
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 2, 3);
+ assertInvalidMessage("Range deletions are not supported for specific columns",
+ "DELETE value FROM %s WHERE partitionKey = ?", 2);
+ }
+ }
+
+ @Test
+ public void testDeleteWithAStaticColumn() throws Throwable
+ {
+ testDeleteWithAStaticColumn(false);
+ testDeleteWithAStaticColumn(true);
+ }
+
+ private void testDeleteWithAStaticColumn(boolean forceFlush) throws Throwable
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering_1 int," +
+ "clustering_2 int," +
+ "value int," +
+ "staticValue text static," +
+ " PRIMARY KEY (partitionKey, clustering_1, clustering_2))");
+
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value, staticValue) VALUES (0, 0, 0, 0, 'A')");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
+ execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value, staticValue) VALUES (1, 0, 0, 6, 'B')");
+ flush(forceFlush);
+
+ execute("DELETE staticValue FROM %s WHERE partitionKey = ?", 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT DISTINCT staticValue FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
+ row(new Object[1]), row("B"));
+
+ execute("DELETE staticValue, value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 1, 0, 0);
+ flush(forceFlush);
+ assertRows(execute("SELECT * FROM %s"),
+ row(1, 0, 0, null, null),
+ row(0, 0, 0, null, 0),
+ row(0, 0, 1, null, 1));
+
+ assertInvalidMessage("Invalid restrictions on clustering columns since the DELETE statement modifies only static columns",
+ "DELETE staticValue FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
+ 0, 0, 1);
+
+ assertInvalidMessage("Invalid restrictions on clustering columns since the DELETE statement modifies only static columns",
+ "DELETE staticValue FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) >= (?, ?)",
+ 0, 0, 1);
+ }
+
+ @Test
+ public void testDeleteWithSecondaryIndices() throws Throwable
+ {
+ testDeleteWithSecondaryIndices(false);
+ testDeleteWithSecondaryIndices(true);
+ }
+
+ private void testDeleteWithSecondaryIndices(boolean forceFlush) throws Throwable
+ {
+ createTable("CREATE TABLE %s (partitionKey int," +
+ "clustering_1 int," +
+ "value int," +
+ "values set<int>," +
+ " PRIMARY KEY (partitionKey, clustering_1))");
+
+ createIndex("CREATE INDEX ON %s (value)");
+ createIndex("CREATE INDEX ON %s (clustering_1)");
+ createIndex("CREATE INDEX ON %s (values)");
+
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 0, 0, {0})");
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 1, 1, {0, 1})");
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 2, 2, {0, 1, 2})");
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 3, 3, {0, 1, 2, 3})");
+ execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (1, 0, 4, {0, 1, 2, 3, 4})");
+
+ flush(forceFlush);
+
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND value = ?", 3, 3, 3);
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values",
+ "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND values CONTAINS ?", 3, 3, 3);
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
+ "DELETE FROM %s WHERE partitionKey = ? AND value = ?", 3, 3);
+ assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values",
+ "DELETE FROM %s WHERE partitionKey = ? AND values CONTAINS ?", 3, 3);
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE clustering_1 = ?", 3);
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE value = ?", 3);
+ assertInvalidMessage("Some partition key parts are missing: partitionkey",
+ "DELETE FROM %s WHERE values CONTAINS ?", 3);
+ }
+
+ @Test
+ public void testDeleteWithOnlyPK() throws Throwable
+ {
+ // This is a regression test for CASSANDRA-11102
+
+ createTable("CREATE TABLE %s (k int, v int, PRIMARY KEY (k, v)) WITH gc_grace_seconds=1");
+
+ execute("INSERT INTO %s(k, v) VALUES (?, ?)", 1, 2);
+
+ execute("DELETE FROM %s WHERE k = ? AND v = ?", 1, 2);
+ execute("INSERT INTO %s(k, v) VALUES (?, ?)", 2, 3);
+
+ Thread.sleep(500);
+
+ execute("DELETE FROM %s WHERE k = ? AND v = ?", 2, 3);
+ execute("INSERT INTO %s(k, v) VALUES (?, ?)", 1, 2);
+
+ Thread.sleep(500);
+
+ flush();
+
+ assertRows(execute("SELECT * FROM %s"), row(1, 2));
+
+ Thread.sleep(1000);
+ compact();
+
+ assertRows(execute("SELECT * FROM %s"), row(1, 2));
+ }
+
+ @Test
+ public void testDeleteColumnNoClustering() throws Throwable
+ {
+ // This is a regression test for CASSANDRA-11068 (and ultimately another test for CASSANDRA-11102)
+ // Creates a table without clustering, insert a row (with a column) and only remove the column.
+ // We should still have a row (with a null column value) even post-compaction.
+
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, v int) WITH gc_grace_seconds=0");
+
+ execute("INSERT INTO %s(k, v) VALUES (?, ?)", 0, 0);
+ execute("DELETE v FROM %s WHERE k=?", 0);
+
+ assertRows(execute("SELECT * FROM %s"), row(0, null));
+
+ flush();
+ assertRows(execute("SELECT * FROM %s"), row(0, null));
+
+ compact();
+ assertRows(execute("SELECT * FROM %s"), row(0, null));
+ }
+ @Test
+ public void testDeleteWithEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY (pk, c))" + options);
+
+ if (StringUtils.isEmpty(options))
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')");
++
++ assertRows(execute("SELECT * FROM %s"),
++ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')");
++
++ assertEmpty(execute("SELECT * FROM %s"));
+ }
+ else
+ {
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('')");
+ assertInvalid("Invalid empty or null value for column c",
+ "DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))");
+ }
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("2"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')");
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')");
++
++ assertRows(execute("SELECT * FROM %s"),
++ row(bytes("foo123"), bytes("1"), bytes("1")),
++ row(bytes("foo123"), bytes("2"), bytes("2")));
+ }
+ }
+
+ @Test
+ public void testDeleteWithMultipleClusteringColumnsAndEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
+ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');");
+
+ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("1"));
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("0"));
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')");
++
++ assertRows(execute("SELECT * FROM %s"),
++ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("0")));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 >= textAsBlob('')");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')");
++
++ assertEmpty(execute("SELECT * FROM %s"));
++
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
++ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("3"));
++
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 <= textAsBlob('')");
++ execute("DELETE FROM %s WHERE pk = textAsBlob('foo123') AND c1 < textAsBlob('')");
++
++ assertRows(execute("SELECT * FROM %s"),
++ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
++ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("3")));
+ }
+ }
++
+ private void flush(boolean forceFlush)
+ {
+ if (forceFlush)
+ flush();
+ }
+
+ @Test
+ public void testDeleteAndReverseQueries() throws Throwable
+ {
+ // This test insert rows in one sstable and a range tombstone covering some of those rows in another, and it
+ // validates we correctly get only the non-removed rows when doing reverse queries.
+
+ createTable("CREATE TABLE %s (k text, i int, PRIMARY KEY (k, i))");
+
+ for (int i = 0; i < 10; i++)
+ execute("INSERT INTO %s(k, i) values (?, ?)", "a", i);
+
+ flush();
+
+ execute("DELETE FROM %s WHERE k = ? AND i >= ? AND i <= ?", "a", 2, 7);
+
+ assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
+ row(9), row(8), row(1), row(0)
+ );
+
+ flush();
+
+ assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", "a"),
+ row(9), row(8), row(1), row(0)
+ );
+ }
}
[10/12] cassandra git commit: Merge branch cassandra-2.2 into
cassandra-3.0
Posted by bl...@apache.org.
Merge branch cassandra-2.2 into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/bb56193a
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/bb56193a
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/bb56193a
Branch: refs/heads/trunk
Commit: bb56193a3f2ac865efb18c9d6944f2927f667771
Parents: 6528fbf 9583b6b
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 16 15:29:42 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 16 15:34:35 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 10 +
.../cassandra/db/compaction/Scrubber.java | 169 ++++-
.../cassandra/db/marshal/ReversedType.java | 10 -
.../db/partitions/AbstractBTreePartition.java | 7 +-
.../db/partitions/ImmutableBTreePartition.java | 34 +-
.../db/rows/UnfilteredRowIterators.java | 28 +
.../org/apache/cassandra/cql3/CQLTester.java | 18 +
.../validation/entities/SecondaryIndexTest.java | 131 ++++
.../cql3/validation/operations/DeleteTest.java | 113 +++
.../operations/SelectOrderByTest.java | 719 ++++++++++---------
.../cql3/validation/operations/SelectTest.java | 357 ++++++++-
.../cassandra/db/marshal/ReversedTypeTest.java | 4 +-
13 files changed, 1210 insertions(+), 391 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index a202755,c421398..7605952
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -49,9 -14,12 +49,10 @@@ Merged from 2.2
* Fixed cqlshlib.test.remove_test_db (CASSANDRA-12214)
* Synchronize ThriftServer::stop() (CASSANDRA-12105)
* Use dedicated thread for JMX notifications (CASSANDRA-12146)
- * NPE when trying to remove purgable tombstones from result (CASSANDRA-12143)
* Improve streaming synchronization and fault tolerance (CASSANDRA-11414)
* MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
- * Don't write shadowed range tombstone (CASSANDRA-12030)
Merged from 2.1:
+ * Fix queries with empty ByteBuffer values in clustering column restrictions (CASSANDRA-12127)
* Disable passing control to post-flush after flush failure to prevent data loss (CASSANDRA-11828)
* Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
* cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index 6b8dfd3,0a3ab36..0bd3920
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -13,55 -13,17 +13,65 @@@ restore snapshots created with the prev
'sstableloader' tool. You can upgrade the file format of your snapshots
using the provided 'sstableupgrade' tool.
-2.2.8
++3.0.9
+ =====
+
+ Upgrading
+ ---------
- - The ReversedType behaviour has been corrected for clustering columns of
- BYTES type containing empty value. Scrub should be run on the existing
- SSTables containing a descending clustering column of BYTES type to correct
- their ordering. See CASSANDRA-12127 for more details.
++ - The ReversedType behaviour has been corrected for clustering columns of
++ BYTES type containing empty value. Scrub should be run on the existing
++ SSTables containing a descending clustering column of BYTES type to correct
++ their ordering. See CASSANDRA-12127 for more details.
+
-2.2.7
+3.0.8
+=====
+
+Upgrading
+---------
+ - Ec2MultiRegionSnitch will no longer automatically set broadcast_rpc_address
+ to the public instance IP if this property is defined on cassandra.yaml.
+
+3.0.7
+=====
+
+Upgrading
+---------
+ - A maximum size for SSTables values has been introduced, to prevent out of memory
+ exceptions when reading corrupt SSTables. This maximum size can be set via
+ max_value_size_in_mb in cassandra.yaml. The default is 256MB, which matches the default
+ value of native_transport_max_frame_size_in_mb. SSTables will be considered corrupt if
+ they contain values whose size exceeds this limit. See CASSANDRA-9530 for more details.
+
+Deprecation
+-----------
+ - DateTieredCompactionStrategy has been deprecated - new tables should use
+ TimeWindowCompactionStrategy. Note that migrating an existing DTCS-table to TWCS might
+ cause increased compaction load for a while after the migration so make sure you run
+ tests before migrating. Read CASSANDRA-9666 for background on this.
+
+New features
+------------
+ - TimeWindowCompactionStrategy has been added. This has proven to be a better approach
+ to time series compaction and new tables should use this instead of DTCS. See
+ CASSANDRA-9666 for details.
+
+3.0.6
+=====
+
+New features
+------------
+ - JSON timestamps are now in UTC and contain the timezone information, see
+ CASSANDRA-11137 for more details.
+
+3.0.5
+=====
+
+Upgrading
+---------
+ - Nothing specific to this release, but please see previous versions upgrading section,
+ especially if you are upgrading from 2.2.
+
+3.0.4
=====
New features
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/compaction/Scrubber.java
index 539c4c7,99ee62e..c010891
--- a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
+++ b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
@@@ -23,11 -23,11 +23,12 @@@ import java.util.*
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
-import com.google.common.collect.AbstractIterator;
++import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
-import org.apache.cassandra.db.columniterator.OnDiskAtomIterator;
+import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.db.partitions.*;
import org.apache.cassandra.io.sstable.*;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.sstable.format.SSTableWriter;
@@@ -327,6 -312,32 +302,38 @@@ public class Scrubber implements Closea
}
}
+ @SuppressWarnings("resource")
+ private boolean tryAppend(DecoratedKey prevKey, DecoratedKey key, SSTableRewriter writer)
+ {
- // OrderCheckerIterator will check, at iteration time, that the cells are in the proper order. If it detects
- // that one cell is out of order, it will stop returning them. The remaining cells will be sorted and added
- // to the outOfOrderRows that will be later written to a new SSTable.
- OrderCheckerIterator atoms = new OrderCheckerIterator(new SSTableIdentityIterator(sstable, dataFile, key, checkData),
- cfs.metadata.comparator.onDiskAtomComparator());
- if (prevKey != null && prevKey.compareTo(key) > 0)
++ // OrderCheckerIterator will check, at iteration time, that the rows are in the proper order. If it detects
++ // that one row is out of order, it will stop returning them. The remaining rows will be sorted and added
++ // to the outOfOrder set that will be later written to a new SSTable.
++ OrderCheckerIterator sstableIterator = new OrderCheckerIterator(new RowMergingSSTableIterator(sstable, dataFile, key),
++ cfs.metadata.comparator);
++
++ try (UnfilteredRowIterator iterator = withValidation(sstableIterator, dataFile.getPath()))
+ {
- saveOutOfOrderRow(prevKey, key, atoms);
- return false;
- }
++ if (prevKey != null && prevKey.compareTo(key) > 0)
++ {
++ saveOutOfOrderRow(prevKey, key, iterator);
++ return false;
++ }
+
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
++ if (writer.tryAppend(iterator) == null)
++ emptyRows++;
++ else
++ goodRows++;
++ }
+
- if (atoms.hasOutOfOrderCells())
- saveOutOfOrderRow(key, atoms);
++ if (sstableIterator.hasRowsOutOfOrder())
++ {
++ outputHandler.warn(String.format("Out of order rows found in partition: %s", key));
++ outOfOrder.add(sstableIterator.getRowsOutOfOrder());
++ }
+
+ return true;
+ }
+
private void updateIndexKey()
{
currentIndexKey = nextIndexKey;
@@@ -469,44 -523,88 +476,146 @@@
}
/**
+ * During 2.x migration, under some circumstances rows might have gotten duplicated.
+ * Merging iterator merges rows with same clustering.
+ *
+ * For more details, refer to CASSANDRA-12144.
+ */
+ private static class RowMergingSSTableIterator extends SSTableIdentityIterator
+ {
+ RowMergingSSTableIterator(SSTableReader sstable, RandomAccessReader file, DecoratedKey key)
+ {
+ super(sstable, file, key);
+ }
+
+ @Override
+ protected Unfiltered doCompute()
+ {
+ if (!iterator.hasNext())
+ return endOfData();
+
+ Unfiltered next = iterator.next();
+ if (!next.isRow())
+ return next;
+
+ while (iterator.hasNext())
+ {
+ Unfiltered peek = iterator.peek();
+ // If there was a duplicate row, merge it.
+ if (next.clustering().equals(peek.clustering()) && peek.isRow())
+ {
+ iterator.next(); // Make sure that the peeked item was consumed.
+ next = Rows.merge((Row) next, (Row) peek, FBUtilities.nowInSeconds());
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return next;
+ }
+ }
++
++ /**
+ * In some case like CASSANDRA-12127 the cells might have been stored in the wrong order. This decorator check the
+ * cells order and collect the out of order cells to correct the problem.
+ */
- private static final class OrderCheckerIterator extends AbstractIterator<OnDiskAtom> implements OnDiskAtomIterator
++ private static final class OrderCheckerIterator extends AbstractIterator<Unfiltered> implements UnfilteredRowIterator
+ {
+ /**
+ * The decorated iterator.
+ */
- private final OnDiskAtomIterator iterator;
++ private final UnfilteredRowIterator iterator;
+
- /**
- * The atom comparator.
- */
- private final Comparator<OnDiskAtom> comparator;
++ private final ClusteringComparator comparator;
+
- /**
- * The Column family containing the cells which are out of order.
- */
- private ColumnFamily outOfOrderCells;
++ private Unfiltered previous;
+
+ /**
- * The previous atom returned
++ * The partition containing the rows which are out of order.
+ */
- private OnDiskAtom previous;
++ private Partition rowsOutOfOrder;
+
- public OrderCheckerIterator(OnDiskAtomIterator iterator, Comparator<OnDiskAtom> comparator)
++ public OrderCheckerIterator(UnfilteredRowIterator iterator, ClusteringComparator comparator)
+ {
+ this.iterator = iterator;
+ this.comparator = comparator;
+ }
+
- public ColumnFamily getColumnFamily()
++ public CFMetaData metadata()
+ {
- return iterator.getColumnFamily();
++ return iterator.metadata();
+ }
+
- public DecoratedKey getKey()
++ public boolean isReverseOrder()
+ {
- return iterator.getKey();
++ return iterator.isReverseOrder();
+ }
+
- public void close() throws IOException
++ public PartitionColumns columns()
+ {
- iterator.close();
++ return iterator.columns();
++ }
++
++ public DecoratedKey partitionKey()
++ {
++ return iterator.partitionKey();
++ }
++
++ public Row staticRow()
++ {
++ return iterator.staticRow();
+ }
+
+ @Override
- protected OnDiskAtom computeNext()
++ public boolean isEmpty()
++ {
++ return iterator.isEmpty();
++ }
++
++ public void close()
++ {
++ iterator.close();
++ }
++
++ public DeletionTime partitionLevelDeletion()
++ {
++ return iterator.partitionLevelDeletion();
++ }
++
++ public EncodingStats stats()
++ {
++ return iterator.stats();
++ }
++
++ public boolean hasRowsOutOfOrder()
++ {
++ return rowsOutOfOrder != null;
++ }
++
++ public Partition getRowsOutOfOrder()
++ {
++ return rowsOutOfOrder;
++ }
++
++ protected Unfiltered computeNext()
+ {
+ if (!iterator.hasNext())
+ return endOfData();
+
- OnDiskAtom next = iterator.next();
++ Unfiltered next = iterator.next();
+
- // If we detect that some cells are out of order we will store and sort the remaining once to insert them
++ // If we detect that some rows are out of order we will store and sort the remaining ones to insert them
+ // in a separate SSTable.
+ if (previous != null && comparator.compare(next, previous) < 0)
+ {
- outOfOrderCells = collectOutOfOrderCells(next, iterator);
++ rowsOutOfOrder = ImmutableBTreePartition.create(UnfilteredRowIterators.concat(next, iterator), false);
+ return endOfData();
+ }
+ previous = next;
+ return next;
+ }
+
- public boolean hasOutOfOrderCells()
- {
- return outOfOrderCells != null;
- }
-
- public ColumnFamily getOutOfOrderCells()
- {
- return outOfOrderCells;
- }
-
- private static ColumnFamily collectOutOfOrderCells(OnDiskAtom atom, OnDiskAtomIterator iterator)
- {
- ColumnFamily cf = iterator.getColumnFamily().cloneMeShallow(ArrayBackedSortedColumns.factory, false);
- cf.addAtom(atom);
- while (iterator.hasNext())
- cf.addAtom(iterator.next());
- return cf;
- }
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/ReversedType.java
index 02320c7,19bee5f..82a1895
--- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
@@@ -66,18 -65,8 +66,8 @@@ public class ReversedType<T> extends Ab
return baseType.isEmptyValueMeaningless();
}
- public int compare(ByteBuffer o1, ByteBuffer o2)
+ public int compareCustom(ByteBuffer o1, ByteBuffer o2)
{
- // An empty byte buffer is always smaller
- if (o1.remaining() == 0)
- {
- return o2.remaining() == 0 ? 0 : -1;
- }
- if (o2.remaining() == 0)
- {
- return 1;
- }
-
return baseType.compare(o2, o1);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
index 1fa3324,0000000..c63acc2
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
@@@ -1,408 -1,0 +1,413 @@@
+/*
+* 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.partitions;
+
+import java.util.Iterator;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.filter.ColumnFilter;
+import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.utils.SearchIterator;
+import org.apache.cassandra.utils.btree.BTree;
+import org.apache.cassandra.utils.btree.BTreeSearchIterator;
+
+import static org.apache.cassandra.utils.btree.BTree.Dir.desc;
+
+public abstract class AbstractBTreePartition implements Partition, Iterable<Row>
+{
+ protected static final Holder EMPTY = new Holder(PartitionColumns.NONE, BTree.empty(), DeletionInfo.LIVE, Rows.EMPTY_STATIC_ROW, EncodingStats.NO_STATS);
+
+ protected final CFMetaData metadata;
+ protected final DecoratedKey partitionKey;
+
+ protected abstract Holder holder();
+ protected abstract boolean canHaveShadowedData();
+
+ protected AbstractBTreePartition(CFMetaData metadata, DecoratedKey partitionKey)
+ {
+ this.metadata = metadata;
+ this.partitionKey = partitionKey;
+ }
+
+ protected static final class Holder
+ {
+ final PartitionColumns columns;
+ final DeletionInfo deletionInfo;
+ // the btree of rows
+ final Object[] tree;
+ final Row staticRow;
+ final EncodingStats stats;
+
+ Holder(PartitionColumns columns, Object[] tree, DeletionInfo deletionInfo, Row staticRow, EncodingStats stats)
+ {
+ this.columns = columns;
+ this.tree = tree;
+ this.deletionInfo = deletionInfo;
+ this.staticRow = staticRow == null ? Rows.EMPTY_STATIC_ROW : staticRow;
+ this.stats = stats;
+ }
+ }
+
+ public DeletionInfo deletionInfo()
+ {
+ return holder().deletionInfo;
+ }
+
+ public Row staticRow()
+ {
+ return holder().staticRow;
+ }
+
+ public boolean isEmpty()
+ {
+ Holder holder = holder();
+ return holder.deletionInfo.isLive() && BTree.isEmpty(holder.tree) && holder.staticRow.isEmpty();
+ }
+
+ public boolean hasRows()
+ {
+ Holder holder = holder();
+ return !BTree.isEmpty(holder.tree);
+ }
+
+ public CFMetaData metadata()
+ {
+ return metadata;
+ }
+
+ public DecoratedKey partitionKey()
+ {
+ return partitionKey;
+ }
+
+ public DeletionTime partitionLevelDeletion()
+ {
+ return holder().deletionInfo.getPartitionDeletion();
+ }
+
+ public PartitionColumns columns()
+ {
+ return holder().columns;
+ }
+
+ public EncodingStats stats()
+ {
+ return holder().stats;
+ }
+
+ public Row getRow(Clustering clustering)
+ {
+ Row row = searchIterator(ColumnFilter.selection(columns()), false).next(clustering);
+ // Note that for statics, this will never return null, this will return an empty row. However,
+ // it's more consistent for this method to return null if we don't really have a static row.
+ return row == null || (clustering == Clustering.STATIC_CLUSTERING && row.isEmpty()) ? null : row;
+ }
+
+ private Row staticRow(Holder current, ColumnFilter columns, boolean setActiveDeletionToRow)
+ {
+ DeletionTime partitionDeletion = current.deletionInfo.getPartitionDeletion();
+ if (columns.fetchedColumns().statics.isEmpty() || (current.staticRow.isEmpty() && partitionDeletion.isLive()))
+ return Rows.EMPTY_STATIC_ROW;
+
+ Row row = current.staticRow.filter(columns, partitionDeletion, setActiveDeletionToRow, metadata);
+ return row == null ? Rows.EMPTY_STATIC_ROW : row;
+ }
+
+ public SearchIterator<Clustering, Row> searchIterator(final ColumnFilter columns, final boolean reversed)
+ {
+ // TODO: we could optimize comparison for "NativeRow" � la #6755
+ final Holder current = holder();
+ return new SearchIterator<Clustering, Row>()
+ {
+ private final SearchIterator<Clustering, Row> rawIter = new BTreeSearchIterator<>(current.tree, metadata.comparator, desc(reversed));
+ private final DeletionTime partitionDeletion = current.deletionInfo.getPartitionDeletion();
+
+ public boolean hasNext()
+ {
+ return rawIter.hasNext();
+ }
+
+ public Row next(Clustering clustering)
+ {
+ if (clustering == Clustering.STATIC_CLUSTERING)
+ return staticRow(current, columns, true);
+
+ Row row = rawIter.next(clustering);
+ RangeTombstone rt = current.deletionInfo.rangeCovering(clustering);
+
+ // A search iterator only return a row, so it doesn't allow to directly account for deletion that should apply to to row
+ // (the partition deletion or the deletion of a range tombstone that covers it). So if needs be, reuse the row deletion
+ // to carry the proper deletion on the row.
+ DeletionTime activeDeletion = partitionDeletion;
+ if (rt != null && rt.deletionTime().supersedes(activeDeletion))
+ activeDeletion = rt.deletionTime();
+
+ if (row == null)
+ return activeDeletion.isLive() ? null : BTreeRow.emptyDeletedRow(clustering, Row.Deletion.regular(activeDeletion));
+
+ return row.filter(columns, activeDeletion, true, metadata);
+ }
+ };
+ }
+
+ public UnfilteredRowIterator unfilteredIterator()
+ {
+ return unfilteredIterator(ColumnFilter.all(metadata()), Slices.ALL, false);
+ }
+
+ public UnfilteredRowIterator unfilteredIterator(ColumnFilter selection, Slices slices, boolean reversed)
+ {
+ return unfilteredIterator(holder(), selection, slices, reversed);
+ }
+
+ public UnfilteredRowIterator unfilteredIterator(Holder current, ColumnFilter selection, Slices slices, boolean reversed)
+ {
+ Row staticRow = staticRow(current, selection, false);
+ if (slices.size() == 0)
+ {
+ DeletionTime partitionDeletion = current.deletionInfo.getPartitionDeletion();
+ return UnfilteredRowIterators.noRowsIterator(metadata, partitionKey, staticRow, partitionDeletion, reversed);
+ }
+
+ return slices.size() == 1
+ ? sliceIterator(selection, slices.get(0), reversed, current, staticRow)
+ : new SlicesIterator(selection, slices, reversed, current, staticRow);
+ }
+
+ private UnfilteredRowIterator sliceIterator(ColumnFilter selection, Slice slice, boolean reversed, Holder current, Row staticRow)
+ {
+ Slice.Bound start = slice.start() == Slice.Bound.BOTTOM ? null : slice.start();
+ Slice.Bound end = slice.end() == Slice.Bound.TOP ? null : slice.end();
+ Iterator<Row> rowIter = BTree.slice(current.tree, metadata.comparator, start, true, end, true, desc(reversed));
+ Iterator<RangeTombstone> deleteIter = current.deletionInfo.rangeIterator(slice, reversed);
+
+ return merge(rowIter, deleteIter, selection, reversed, current, staticRow);
+ }
+
+ private RowAndDeletionMergeIterator merge(Iterator<Row> rowIter, Iterator<RangeTombstone> deleteIter,
+ ColumnFilter selection, boolean reversed, Holder current, Row staticRow)
+ {
+ return new RowAndDeletionMergeIterator(metadata, partitionKey, current.deletionInfo.getPartitionDeletion(),
+ selection, staticRow, reversed, current.stats,
+ rowIter, deleteIter,
+ canHaveShadowedData());
+ }
+
+ private abstract class AbstractIterator extends AbstractUnfilteredRowIterator
+ {
+ final Holder current;
+ final ColumnFilter selection;
+
+ private AbstractIterator(ColumnFilter selection, boolean isReversed)
+ {
+ this(AbstractBTreePartition.this.holder(), selection, isReversed);
+ }
+
+ private AbstractIterator(Holder current, ColumnFilter selection, boolean isReversed)
+ {
+ this(current,
+ AbstractBTreePartition.this.staticRow(current, selection, false),
+ selection, isReversed);
+ }
+
+ private AbstractIterator(Holder current, Row staticRow, ColumnFilter selection, boolean isReversed)
+ {
+ super(AbstractBTreePartition.this.metadata,
+ AbstractBTreePartition.this.partitionKey,
+ current.deletionInfo.getPartitionDeletion(),
+ selection.fetchedColumns(), // non-selected columns will be filtered in subclasses by RowAndDeletionMergeIterator
+ // it would also be more precise to return the intersection of the selection and current.columns,
+ // but its probably not worth spending time on computing that.
+ staticRow,
+ isReversed,
+ current.stats);
+ this.current = current;
+ this.selection = selection;
+ }
+ }
+
+ public class SlicesIterator extends AbstractIterator
+ {
+ private final Slices slices;
+
+ private int idx;
+ private Iterator<Unfiltered> currentSlice;
+
+ private SlicesIterator(ColumnFilter selection,
+ Slices slices,
+ boolean isReversed,
+ Holder current,
+ Row staticRow)
+ {
+ super(current, staticRow, selection, isReversed);
+ this.slices = slices;
+ }
+
+ protected Unfiltered computeNext()
+ {
+ while (true)
+ {
+ if (currentSlice == null)
+ {
+ if (idx >= slices.size())
+ return endOfData();
+
+ int sliceIdx = isReverseOrder ? slices.size() - idx - 1 : idx;
+ currentSlice = sliceIterator(selection, slices.get(sliceIdx), isReverseOrder, current, Rows.EMPTY_STATIC_ROW);
+ idx++;
+ }
+
+ if (currentSlice.hasNext())
+ return currentSlice.next();
+
+ currentSlice = null;
+ }
+ }
+ }
+
+ public class SliceableIterator extends AbstractIterator implements SliceableUnfilteredRowIterator
+ {
+ private Iterator<Unfiltered> iterator;
+
+ protected SliceableIterator(ColumnFilter selection, boolean isReversed)
+ {
+ super(selection, isReversed);
+ }
+
+ protected Unfiltered computeNext()
+ {
+ if (iterator == null)
+ iterator = unfilteredIterator(selection, Slices.ALL, isReverseOrder);
+ if (!iterator.hasNext())
+ return endOfData();
+ return iterator.next();
+ }
+
+ public Iterator<Unfiltered> slice(Slice slice)
+ {
+ return sliceIterator(selection, slice, isReverseOrder, current, staticRow);
+ }
+ }
+
+ public SliceableUnfilteredRowIterator sliceableUnfilteredIterator(ColumnFilter columns, boolean reversed)
+ {
+ return new SliceableIterator(columns, reversed);
+ }
+
+ protected SliceableUnfilteredRowIterator sliceableUnfilteredIterator()
+ {
+ return sliceableUnfilteredIterator(ColumnFilter.all(metadata), false);
+ }
+
+ protected static Holder build(UnfilteredRowIterator iterator, int initialRowCapacity)
+ {
++ return build(iterator, initialRowCapacity, true);
++ }
++
++ protected static Holder build(UnfilteredRowIterator iterator, int initialRowCapacity, boolean ordered)
++ {
+ CFMetaData metadata = iterator.metadata();
+ PartitionColumns columns = iterator.columns();
+ boolean reversed = iterator.isReverseOrder();
+
+ BTree.Builder<Row> builder = BTree.builder(metadata.comparator, initialRowCapacity);
- builder.auto(false);
++ builder.auto(!ordered);
+ MutableDeletionInfo.Builder deletionBuilder = MutableDeletionInfo.builder(iterator.partitionLevelDeletion(), metadata.comparator, reversed);
+
+ while (iterator.hasNext())
+ {
+ Unfiltered unfiltered = iterator.next();
+ if (unfiltered.kind() == Unfiltered.Kind.ROW)
+ builder.add((Row)unfiltered);
+ else
+ deletionBuilder.add((RangeTombstoneMarker)unfiltered);
+ }
+
+ if (reversed)
+ builder.reverse();
+
+ return new Holder(columns, builder.build(), deletionBuilder.build(), iterator.staticRow(), iterator.stats());
+ }
+
+ // Note that when building with a RowIterator, deletion will generally be LIVE, but we allow to pass it nonetheless because PartitionUpdate
+ // passes a MutableDeletionInfo that it mutates later.
+ protected static Holder build(RowIterator rows, DeletionInfo deletion, boolean buildEncodingStats, int initialRowCapacity)
+ {
+ CFMetaData metadata = rows.metadata();
+ PartitionColumns columns = rows.columns();
+ boolean reversed = rows.isReverseOrder();
+
+ BTree.Builder<Row> builder = BTree.builder(metadata.comparator, initialRowCapacity);
+ builder.auto(false);
+ while (rows.hasNext())
+ {
+ Row row = rows.next();
+ builder.add(row);
+ }
+
+ if (reversed)
+ builder.reverse();
+
+ Row staticRow = rows.staticRow();
+ Object[] tree = builder.build();
+ EncodingStats stats = buildEncodingStats ? EncodingStats.Collector.collect(staticRow, BTree.iterator(tree), deletion)
+ : EncodingStats.NO_STATS;
+ return new Holder(columns, tree, deletion, staticRow, stats);
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(String.format("[%s.%s] key=%s columns=%s",
+ metadata.ksName,
+ metadata.cfName,
+ metadata.getKeyValidator().getString(partitionKey().getKey()),
+ columns()));
+
+ if (staticRow() != Rows.EMPTY_STATIC_ROW)
+ sb.append("\n ").append(staticRow().toString(metadata));
+
+ for (Row row : this)
+ sb.append("\n ").append(row.toString(metadata));
+
+ return sb.toString();
+ }
+
+ public int rowCount()
+ {
+ return BTree.size(holder().tree);
+ }
+
+ public Iterator<Row> iterator()
+ {
+ return BTree.<Row>iterator(holder().tree);
+ }
+
+ public Row lastRow()
+ {
+ Object[] tree = holder().tree;
+ if (BTree.isEmpty(tree))
+ return null;
+
+ return BTree.findByIndex(tree, BTree.size(tree) - 1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/partitions/ImmutableBTreePartition.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/partitions/ImmutableBTreePartition.java
index 9af4bad,0000000..8d96f1e
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/partitions/ImmutableBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/ImmutableBTreePartition.java
@@@ -1,91 -1,0 +1,123 @@@
+/*
+* 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.partitions;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.DeletionInfo;
+import org.apache.cassandra.db.PartitionColumns;
+import org.apache.cassandra.db.rows.*;
+
+public class ImmutableBTreePartition extends AbstractBTreePartition
+{
+
+ protected final Holder holder;
+
+ public ImmutableBTreePartition(CFMetaData metadata,
+ DecoratedKey partitionKey,
+ PartitionColumns columns,
+ Row staticRow,
+ Object[] tree,
+ DeletionInfo deletionInfo,
+ EncodingStats stats)
+ {
+ super(metadata, partitionKey);
+ this.holder = new Holder(columns, tree, deletionInfo, staticRow, stats);
+ }
+
+ protected ImmutableBTreePartition(CFMetaData metadata,
+ DecoratedKey partitionKey,
+ Holder holder)
+ {
+ super(metadata, partitionKey);
+ this.holder = holder;
+ }
+
+ /**
+ * Creates an {@code ImmutableBTreePartition} holding all the data of the provided iterator.
+ *
+ * Warning: Note that this method does not close the provided iterator and it is
+ * up to the caller to do so.
+ *
+ * @param iterator the iterator to gather in memory.
+ * @return the created partition.
+ */
+ public static ImmutableBTreePartition create(UnfilteredRowIterator iterator)
+ {
+ return create(iterator, 16);
+ }
+
+ /**
+ * Creates an {@code ImmutableBTreePartition} holding all the data of the provided iterator.
+ *
+ * Warning: Note that this method does not close the provided iterator and it is
+ * up to the caller to do so.
+ *
+ * @param iterator the iterator to gather in memory.
++ * @param ordered {@code true} if the iterator will return the rows in order, {@code false} otherwise.
++ * @return the created partition.
++ */
++ public static ImmutableBTreePartition create(UnfilteredRowIterator iterator, boolean ordered)
++ {
++ return create(iterator, 16, ordered);
++ }
++
++ /**
++ * Creates an {@code ImmutableBTreePartition} holding all the data of the provided iterator.
++ *
++ * Warning: Note that this method does not close the provided iterator and it is
++ * up to the caller to do so.
++ *
++ * @param iterator the iterator to gather in memory.
+ * @param initialRowCapacity sizing hint (in rows) to use for the created partition. It should ideally
+ * correspond or be a good estimation of the number or rows in {@code iterator}.
+ * @return the created partition.
+ */
+ public static ImmutableBTreePartition create(UnfilteredRowIterator iterator, int initialRowCapacity)
+ {
- return new ImmutableBTreePartition(iterator.metadata(), iterator.partitionKey(), build(iterator, initialRowCapacity));
++ return create(iterator, initialRowCapacity, true);
++ }
++
++ /**
++ * Creates an {@code ImmutableBTreePartition} holding all the data of the provided iterator.
++ *
++ * Warning: Note that this method does not close the provided iterator and it is
++ * up to the caller to do so.
++ *
++ * @param iterator the iterator to gather in memory.
++ * @param initialRowCapacity sizing hint (in rows) to use for the created partition. It should ideally
++ * correspond or be a good estimation of the number or rows in {@code iterator}.
++ * @param ordered {@code true} if the iterator will return the rows in order, {@code false} otherwise.
++ * @return the created partition.
++ */
++ public static ImmutableBTreePartition create(UnfilteredRowIterator iterator, int initialRowCapacity, boolean ordered)
++ {
++ return new ImmutableBTreePartition(iterator.metadata(), iterator.partitionKey(), build(iterator, initialRowCapacity, ordered));
+ }
+
+ protected Holder holder()
+ {
+ return holder;
+ }
+
+ protected boolean canHaveShadowedData()
+ {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
index 3218ff2,0000000..43653a9
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredRowIterators.java
@@@ -1,558 -1,0 +1,586 @@@
+/*
+ * 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.rows;
+
+import java.util.*;
+import java.security.MessageDigest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.transform.FilteredRows;
+import org.apache.cassandra.db.transform.MoreRows;
+import org.apache.cassandra.db.transform.Transformation;
+import org.apache.cassandra.io.util.FileUtils;
+import org.apache.cassandra.io.sstable.CorruptSSTableException;
+import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.net.MessagingService;
+import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.IMergeIterator;
+import org.apache.cassandra.utils.MergeIterator;
+import org.apache.cassandra.utils.memory.AbstractAllocator;
+
+/**
+ * Static methods to work with atom iterators.
+ */
+public abstract class UnfilteredRowIterators
+{
+ private static final Logger logger = LoggerFactory.getLogger(UnfilteredRowIterators.class);
+
+ private UnfilteredRowIterators() {}
+
+ /**
+ * Interface for a listener interested in the result of merging multiple versions of a given row.
+ * <p>
+ * Implementors of this interface are given enough information that they can easily reconstruct the difference
+ * between the merged result and each individual input. This is used when reconciling results on replias for
+ * instance to figure out what to send as read-repair to each source.
+ */
+ public interface MergeListener
+ {
+ /**
+ * Called once for the merged partition.
+ *
+ * @param mergedDeletion the partition level deletion for the merged partition. Implementors can test if the
+ * merged partition actually has a partition level deletion or not by calling {@code mergedDeletion.isLive()}.
+ * @param versions the partition level deletion for the sources of the merge. Elements of the array will never
+ * be null, but be "live".
+ **/
+ public void onMergedPartitionLevelDeletion(DeletionTime mergedDeletion, DeletionTime[] versions);
+
+ /**
+ * Called once for every row participating in the merge.
+ * <p>
+ * Note that this is called for every clustering where at least one of the source merged has a row. In
+ * particular, this may be called in cases where there is no row in the merged output (if a source has a row
+ * that is shadowed by another source range tombstone or partition level deletion).
+ *
+ * @param merged the result of the merge. This cannot be {@code null} but can be empty, in which case this is a
+ * placeholder for when at least one source has a row, but that row is shadowed in the merged output.
+ * @param versions for each source, the row in that source corresponding to {@code merged}. This can be
+ * {@code null} for some sources if the source has not such row.
+ */
+ public void onMergedRows(Row merged, Row[] versions);
+
+ /**
+ * Called once for every range tombstone marker participating in the merge.
+ * <p>
+ * Note that this is called for every "clustering position" where at least one of the source merged has a range
+ * tombstone marker.
+ *
+ * @param merged the marker in the merged output. This can be {@code null} if there is no such marker, which
+ * means that at least one source has a marker in {@code versions} but the merged out has nothing corresponding
+ * (this basically mean the merged output has a currently open deletion that shadows whatever marker the source
+ * had).
+ * @param versions the marker for each source merged. This can be {@code null} for some source if that source
+ * has not such marker.
+ */
+ public void onMergedRangeTombstoneMarkers(RangeTombstoneMarker merged, RangeTombstoneMarker[] versions);
+
+ public void close();
+ }
+
+ /**
+ * Returns a iterator that only returns rows with only live content.
+ *
+ * This is mainly used in the CQL layer when we know we don't care about deletion
+ * infos (and since an UnfilteredRowIterator cannot shadow it's own data, we know everyting
+ * returned isn't shadowed by a tombstone).
+ */
+ public static RowIterator filter(UnfilteredRowIterator iter, int nowInSec)
+ {
+ return FilteredRows.filter(iter, nowInSec);
+ }
+
+ /**
+ * Returns an iterator that is the result of merging other iterators.
+ */
+ public static UnfilteredRowIterator merge(List<UnfilteredRowIterator> iterators, int nowInSec)
+ {
+ assert !iterators.isEmpty();
+ if (iterators.size() == 1)
+ return iterators.get(0);
+
+ return UnfilteredRowMergeIterator.create(iterators, nowInSec, null);
+ }
+
+ /**
+ * Returns an iterator that is the result of merging other iterators, and (optionally) using
+ * specific MergeListener.
+ *
+ * Note that this method assumes that there is at least 2 iterators to merge.
+ */
+ public static UnfilteredRowIterator merge(List<UnfilteredRowIterator> iterators, int nowInSec, MergeListener mergeListener)
+ {
+ return UnfilteredRowMergeIterator.create(iterators, nowInSec, mergeListener);
+ }
+
+ /**
+ * Returns an empty unfiltered iterator for a given partition.
+ */
+ public static UnfilteredRowIterator noRowsIterator(final CFMetaData cfm, final DecoratedKey partitionKey, final Row staticRow, final DeletionTime partitionDeletion, final boolean isReverseOrder)
+ {
+ return EmptyIterators.unfilteredRow(cfm, partitionKey, isReverseOrder, staticRow, partitionDeletion);
+ }
+
+ /**
+ * Digests the partition represented by the provided iterator.
+ *
+ * @param command the command that has yield {@code iterator}. This can be null if {@code version >= MessagingService.VERSION_30}
+ * as this is only used when producing digest to be sent to legacy nodes.
+ * @param iterator the iterator to digest.
+ * @param digest the {@code MessageDigest} to use for the digest.
+ * @param version the messaging protocol to use when producing the digest.
+ */
+ public static void digest(ReadCommand command, UnfilteredRowIterator iterator, MessageDigest digest, int version)
+ {
+ if (version < MessagingService.VERSION_30)
+ {
+ LegacyLayout.fromUnfilteredRowIterator(command, iterator).digest(iterator.metadata(), digest);
+ return;
+ }
+
+ digest.update(iterator.partitionKey().getKey().duplicate());
+ iterator.partitionLevelDeletion().digest(digest);
+ iterator.columns().regulars.digest(digest);
+ // When serializing an iterator, we skip the static columns if the iterator has not static row, even if the
+ // columns() object itself has some (the columns() is a superset of what the iterator actually contains, and
+ // will correspond to the queried columns pre-serialization). So we must avoid taking the satic column names
+ // into account if there is no static row or we'd have a digest mismatch between depending on whether the digest
+ // is computed on an iterator that has been serialized or not (see CASSANDRA-12090)
+ // TODO: in practice we could completely skip digesting the columns since they are more informative of what the
+ // iterator may contain, and digesting the actual content is enough. And in fact, that would be more correct
+ // (since again, the columns could be different without the information represented by the iterator being
+ // different), but removing them entirely is stricly speaking a breaking change (it would create mismatches on
+ // upgrade) so we can only do on the next protocol version bump.
+ if (iterator.staticRow() != Rows.EMPTY_STATIC_ROW)
+ iterator.columns().statics.digest(digest);
+ FBUtilities.updateWithBoolean(digest, iterator.isReverseOrder());
+ iterator.staticRow().digest(digest);
+
+ while (iterator.hasNext())
+ {
+ Unfiltered unfiltered = iterator.next();
+ unfiltered.digest(digest);
+ }
+ }
+
+ /**
+ * Returns an iterator that concatenate two atom iterators.
+ * This method assumes that both iterator are from the same partition and that the atom from
+ * {@code iter2} come after the ones of {@code iter1} (that is, that concatenating the iterator
+ * make sense).
+ */
+ public static UnfilteredRowIterator concat(final UnfilteredRowIterator iter1, final UnfilteredRowIterator iter2)
+ {
+ assert iter1.metadata().cfId.equals(iter2.metadata().cfId)
+ && iter1.partitionKey().equals(iter2.partitionKey())
+ && iter1.partitionLevelDeletion().equals(iter2.partitionLevelDeletion())
+ && iter1.isReverseOrder() == iter2.isReverseOrder()
+ && iter1.columns().equals(iter2.columns())
+ && iter1.staticRow().equals(iter2.staticRow());
+
+ class Extend implements MoreRows<UnfilteredRowIterator>
+ {
+ boolean returned = false;
+ public UnfilteredRowIterator moreContents()
+ {
+ if (returned)
+ return null;
+ returned = true;
+ return iter2;
+ }
+ }
+
+ return MoreRows.extend(iter1, new Extend());
+ }
+
++ /**
++ * Returns an iterator that concatenate the specified atom with the iterator.
++ */
++ public static UnfilteredRowIterator concat(final Unfiltered first, final UnfilteredRowIterator rest)
++ {
++ return new WrappingUnfilteredRowIterator(rest)
++ {
++ private boolean hasReturnedFirst;
++
++ @Override
++ public boolean hasNext()
++ {
++ return hasReturnedFirst ? super.hasNext() : true;
++ }
++
++ @Override
++ public Unfiltered next()
++ {
++ if (!hasReturnedFirst)
++ {
++ hasReturnedFirst = true;
++ return first;
++ }
++ return super.next();
++ }
++ };
++ }
++
+ public static UnfilteredRowIterator cloningIterator(UnfilteredRowIterator iterator, final AbstractAllocator allocator)
+ {
+ class Cloner extends Transformation
+ {
+ private final Row.Builder builder = allocator.cloningBTreeRowBuilder();
+
+ public Row applyToStatic(Row row)
+ {
+ return Rows.copy(row, builder).build();
+ }
+
+ @Override
+ public Row applyToRow(Row row)
+ {
+ return Rows.copy(row, builder).build();
+ }
+
+ @Override
+ public RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
+ {
+ return marker.copy(allocator);
+ }
+ }
+ return Transformation.apply(iterator, new Cloner());
+ }
+
+ /**
+ * Validate that the data of the provided iterator is valid, that is that the values
+ * it contains are valid for the type they represent, and more generally that the
+ * infos stored are sensible.
+ *
+ * This is mainly used by scrubber to detect problems in sstables.
+ *
+ * @param iterator the partition to check.
+ * @param filename the name of the file the data is comming from.
+ * @return an iterator that returns the same data than {@code iterator} but that
+ * checks said data and throws a {@code CorruptedSSTableException} if it detects
+ * invalid data.
+ */
+ public static UnfilteredRowIterator withValidation(UnfilteredRowIterator iterator, final String filename)
+ {
+ class Validator extends Transformation
+ {
+ @Override
+ public Row applyToStatic(Row row)
+ {
+ validate(row);
+ return row;
+ }
+
+ @Override
+ public Row applyToRow(Row row)
+ {
+ validate(row);
+ return row;
+ }
+
+ @Override
+ public RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
+ {
+ validate(marker);
+ return marker;
+ }
+
+ private void validate(Unfiltered unfiltered)
+ {
+ try
+ {
+ unfiltered.validateData(iterator.metadata());
+ }
+ catch (MarshalException me)
+ {
+ throw new CorruptSSTableException(me, filename);
+ }
+ }
+ }
+ return Transformation.apply(iterator, new Validator());
+ }
+
+ /**
+ * Wraps the provided iterator so it logs the returned atoms for debugging purposes.
+ * <p>
+ * Note that this is only meant for debugging as this can log a very large amount of
+ * logging at INFO.
+ */
+ public static UnfilteredRowIterator loggingIterator(UnfilteredRowIterator iterator, final String id, final boolean fullDetails)
+ {
+ CFMetaData metadata = iterator.metadata();
+ logger.info("[{}] Logging iterator on {}.{}, partition key={}, reversed={}, deletion={}",
+ id,
+ metadata.ksName,
+ metadata.cfName,
+ metadata.getKeyValidator().getString(iterator.partitionKey().getKey()),
+ iterator.isReverseOrder(),
+ iterator.partitionLevelDeletion().markedForDeleteAt());
+
+ class Logger extends Transformation
+ {
+ @Override
+ public Row applyToStatic(Row row)
+ {
+ if (!row.isEmpty())
+ logger.info("[{}] {}", id, row.toString(metadata, fullDetails));
+ return row;
+ }
+
+ @Override
+ public Row applyToRow(Row row)
+ {
+ logger.info("[{}] {}", id, row.toString(metadata, fullDetails));
+ return row;
+ }
+
+ @Override
+ public RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
+ {
+ logger.info("[{}] {}", id, marker.toString(metadata));
+ return marker;
+ }
+ }
+ return Transformation.apply(iterator, new Logger());
+ }
+
+ /**
+ * A wrapper over MergeIterator to implement the UnfilteredRowIterator interface.
+ */
+ private static class UnfilteredRowMergeIterator extends AbstractUnfilteredRowIterator
+ {
+ private final IMergeIterator<Unfiltered, Unfiltered> mergeIterator;
+ private final MergeListener listener;
+
+ private UnfilteredRowMergeIterator(CFMetaData metadata,
+ List<UnfilteredRowIterator> iterators,
+ PartitionColumns columns,
+ DeletionTime partitionDeletion,
+ int nowInSec,
+ boolean reversed,
+ MergeListener listener)
+ {
+ super(metadata,
+ iterators.get(0).partitionKey(),
+ partitionDeletion,
+ columns,
+ mergeStaticRows(iterators, columns.statics, nowInSec, listener, partitionDeletion),
+ reversed,
+ mergeStats(iterators));
+
+ this.mergeIterator = MergeIterator.get(iterators,
+ reversed ? metadata.comparator.reversed() : metadata.comparator,
+ new MergeReducer(iterators.size(), reversed, nowInSec, listener));
+ this.listener = listener;
+ }
+
+ private static UnfilteredRowMergeIterator create(List<UnfilteredRowIterator> iterators, int nowInSec, MergeListener listener)
+ {
+ try
+ {
+ checkForInvalidInput(iterators);
+ return new UnfilteredRowMergeIterator(iterators.get(0).metadata(),
+ iterators,
+ collectColumns(iterators),
+ collectPartitionLevelDeletion(iterators, listener),
+ nowInSec,
+ iterators.get(0).isReverseOrder(),
+ listener);
+ }
+ catch (RuntimeException | Error e)
+ {
+ try
+ {
+ FBUtilities.closeAll(iterators);
+ }
+ catch (Exception suppressed)
+ {
+ e.addSuppressed(suppressed);
+ }
+ throw e;
+ }
+ }
+
+ @SuppressWarnings("resource") // We're not really creating any resource here
+ private static void checkForInvalidInput(List<UnfilteredRowIterator> iterators)
+ {
+ if (iterators.isEmpty())
+ return;
+
+ UnfilteredRowIterator first = iterators.get(0);
+ for (int i = 1; i < iterators.size(); i++)
+ {
+ UnfilteredRowIterator iter = iterators.get(i);
+ assert first.metadata().cfId.equals(iter.metadata().cfId);
+ assert first.partitionKey().equals(iter.partitionKey());
+ assert first.isReverseOrder() == iter.isReverseOrder();
+ }
+ }
+
+ @SuppressWarnings("resource") // We're not really creating any resource here
+ private static DeletionTime collectPartitionLevelDeletion(List<UnfilteredRowIterator> iterators, MergeListener listener)
+ {
+ DeletionTime[] versions = listener == null ? null : new DeletionTime[iterators.size()];
+
+ DeletionTime delTime = DeletionTime.LIVE;
+ for (int i = 0; i < iterators.size(); i++)
+ {
+ UnfilteredRowIterator iter = iterators.get(i);
+ DeletionTime iterDeletion = iter.partitionLevelDeletion();
+ if (listener != null)
+ versions[i] = iterDeletion;
+ if (!delTime.supersedes(iterDeletion))
+ delTime = iterDeletion;
+ }
+ if (listener != null)
+ listener.onMergedPartitionLevelDeletion(delTime, versions);
+ return delTime;
+ }
+
+ private static Row mergeStaticRows(List<UnfilteredRowIterator> iterators,
+ Columns columns,
+ int nowInSec,
+ MergeListener listener,
+ DeletionTime partitionDeletion)
+ {
+ if (columns.isEmpty())
+ return Rows.EMPTY_STATIC_ROW;
+
+ if (iterators.stream().allMatch(iter -> iter.staticRow().isEmpty()))
+ return Rows.EMPTY_STATIC_ROW;
+
+ Row.Merger merger = new Row.Merger(iterators.size(), nowInSec, columns.hasComplex());
+ for (int i = 0; i < iterators.size(); i++)
+ merger.add(i, iterators.get(i).staticRow());
+
+ Row merged = merger.merge(partitionDeletion);
+ if (merged == null)
+ merged = Rows.EMPTY_STATIC_ROW;
+ if (listener != null)
+ listener.onMergedRows(merged, merger.mergedRows());
+ return merged;
+ }
+
+ private static PartitionColumns collectColumns(List<UnfilteredRowIterator> iterators)
+ {
+ PartitionColumns first = iterators.get(0).columns();
+ Columns statics = first.statics;
+ Columns regulars = first.regulars;
+ for (int i = 1; i < iterators.size(); i++)
+ {
+ PartitionColumns cols = iterators.get(i).columns();
+ statics = statics.mergeTo(cols.statics);
+ regulars = regulars.mergeTo(cols.regulars);
+ }
+ return statics == first.statics && regulars == first.regulars
+ ? first
+ : new PartitionColumns(statics, regulars);
+ }
+
+ private static EncodingStats mergeStats(List<UnfilteredRowIterator> iterators)
+ {
+ EncodingStats stats = EncodingStats.NO_STATS;
+ for (UnfilteredRowIterator iter : iterators)
+ stats = stats.mergeWith(iter.stats());
+ return stats;
+ }
+
+ protected Unfiltered computeNext()
+ {
+ while (mergeIterator.hasNext())
+ {
+ Unfiltered merged = mergeIterator.next();
+ if (merged != null)
+ return merged;
+ }
+ return endOfData();
+ }
+
+ public void close()
+ {
+ // This will close the input iterators
+ FileUtils.closeQuietly(mergeIterator);
+
+ if (listener != null)
+ listener.close();
+ }
+
+ private class MergeReducer extends MergeIterator.Reducer<Unfiltered, Unfiltered>
+ {
+ private final MergeListener listener;
+
+ private Unfiltered.Kind nextKind;
+
+ private final Row.Merger rowMerger;
+ private final RangeTombstoneMarker.Merger markerMerger;
+
+ private MergeReducer(int size, boolean reversed, int nowInSec, MergeListener listener)
+ {
+ this.rowMerger = new Row.Merger(size, nowInSec, columns().regulars.hasComplex());
+ this.markerMerger = new RangeTombstoneMarker.Merger(size, partitionLevelDeletion(), reversed);
+ this.listener = listener;
+ }
+
+ @Override
+ public boolean trivialReduceIsTrivial()
+ {
+ // If we have a listener, we must signal it even when we have a single version
+ return listener == null;
+ }
+
+ public void reduce(int idx, Unfiltered current)
+ {
+ nextKind = current.kind();
+ if (nextKind == Unfiltered.Kind.ROW)
+ rowMerger.add(idx, (Row)current);
+ else
+ markerMerger.add(idx, (RangeTombstoneMarker)current);
+ }
+
+ protected Unfiltered getReduced()
+ {
+ if (nextKind == Unfiltered.Kind.ROW)
+ {
+ Row merged = rowMerger.merge(markerMerger.activeDeletion());
+ if (listener != null)
+ listener.onMergedRows(merged == null ? BTreeRow.emptyRow(rowMerger.mergedClustering()) : merged, rowMerger.mergedRows());
+ return merged;
+ }
+ else
+ {
+ RangeTombstoneMarker merged = markerMerger.merge();
+ if (listener != null)
+ listener.onMergedRangeTombstoneMarkers(merged, markerMerger.mergedMarkers());
+ return merged;
+ }
+ }
+
+ protected void onKeyChange()
+ {
+ if (nextKind == Unfiltered.Kind.ROW)
+ rowMerger.clear();
+ else
+ markerMerger.clear();
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/CQLTester.java
index e3dc220,98b8e23..c9c4631
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@@ -366,21 -278,20 +366,39 @@@ public abstract class CQLTeste
return list.isEmpty() ? Collections.<String>emptyList() : new ArrayList<>(list);
}
+ public ColumnFamilyStore getCurrentColumnFamilyStore()
+ {
+ String currentTable = currentTable();
+ return currentTable == null
+ ? null
+ : Keyspace.open(KEYSPACE).getColumnFamilyStore(currentTable);
+ }
+
public void flush()
{
- try
- {
- String currentTable = currentTable();
- if (currentTable != null)
- Keyspace.open(KEYSPACE).getColumnFamilyStore(currentTable).forceFlush().get();
- }
- catch (InterruptedException | ExecutionException e)
- {
- throw new RuntimeException(e);
- }
+ ColumnFamilyStore store = getCurrentColumnFamilyStore();
+ if (store != null)
+ store.forceBlockingFlush();
+ }
+
++ @FunctionalInterface
++ public interface CheckedFunction {
++ void apply() throws Throwable;
++ }
++
++ /**
++ * Runs the given function before and after a flush of sstables. This is useful for checking that behavior is
++ * the same whether data is in memtables or sstables.
++ * @param runnable
++ * @throws Throwable
++ */
++ public void beforeAndAfterFlush(CheckedFunction runnable) throws Throwable
++ {
++ runnable.apply();
++ flush();
++ runnable.apply();
+ }
+
public void compact()
{
try
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bb56193a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
index f9802d7,11d2462..0cf13bd
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
@@@ -17,47 -17,38 +17,49 @@@
*/
package org.apache.cassandra.cql3.validation.entities;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
+import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Test;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
-import org.apache.cassandra.db.ColumnFamily;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
+import org.apache.cassandra.cql3.statements.IndexTarget;
import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.db.DecoratedKey;
-import org.apache.cassandra.db.IndexExpression;
-import org.apache.cassandra.db.composites.CellName;
-import org.apache.cassandra.db.index.IndexNotAvailableException;
-import org.apache.cassandra.db.index.PerRowSecondaryIndex;
-import org.apache.cassandra.db.index.SecondaryIndexSearcher;
-import org.apache.cassandra.db.index.composites.CompositesSearcher;
+import org.apache.cassandra.db.DeletionTime;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.rows.Cell;
+import org.apache.cassandra.db.rows.Row;
import org.apache.cassandra.exceptions.ConfigurationException;
-import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.SyntaxException;
-import org.apache.cassandra.utils.FBUtilities;
-import org.apache.cassandra.utils.concurrent.OpOrder.Group;
-import org.apache.commons.lang3.StringUtils;
-import org.junit.Test;
-
+import org.apache.cassandra.index.IndexNotAvailableException;
+import org.apache.cassandra.index.SecondaryIndexManager;
+import org.apache.cassandra.index.StubIndex;
+import org.apache.cassandra.index.internal.CustomCassandraIndex;
+import org.apache.cassandra.schema.IndexMetadata;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.messages.ResultMessage;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.MD5Digest;
+import org.apache.cassandra.utils.Pair;
+
+import static org.apache.cassandra.Util.throwAssert;
+ import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class SecondaryIndexTest extends CQLTester
{
@@@ -949,77 -684,190 +951,206 @@@
}
@Test
+ public void droppingIndexInvalidatesPreparedStatements() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY ((a), b))");
+ createIndex("CREATE INDEX c_idx ON %s(c)");
+ MD5Digest cqlId = prepareStatement("SELECT * FROM %s.%s WHERE c=?", false).statementId;
+ Integer thriftId = prepareStatement("SELECT * FROM %s.%s WHERE c=?", true).toThriftPreparedResult().getItemId();
+
+ assertNotNull(QueryProcessor.instance.getPrepared(cqlId));
+ assertNotNull(QueryProcessor.instance.getPreparedForThrift(thriftId));
+
+ dropIndex("DROP INDEX %s.c_idx");
+
+ assertNull(QueryProcessor.instance.getPrepared(cqlId));
+ assertNull(QueryProcessor.instance.getPreparedForThrift(thriftId));
+ }
+
+ // See CASSANDRA-11021
+ @Test
+ public void testIndexesOnNonStaticColumnsWhereSchemaIncludesStaticColumns() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int static, d int, PRIMARY KEY (a, b))");
+ createIndex("CREATE INDEX b_idx on %s(b)");
+ createIndex("CREATE INDEX d_idx on %s(d)");
+
+ execute("INSERT INTO %s (a, b, c ,d) VALUES (0, 0, 0, 0)");
+ execute("INSERT INTO %s (a, b, c, d) VALUES (1, 1, 1, 1)");
+ assertRows(execute("SELECT * FROM %s WHERE b = 0"), row(0, 0, 0, 0));
+ assertRows(execute("SELECT * FROM %s WHERE d = 1"), row(1, 1, 1, 1));
+
+ execute("UPDATE %s SET c = 2 WHERE a = 0");
+ execute("UPDATE %s SET c = 3, d = 4 WHERE a = 1 AND b = 1");
+ assertRows(execute("SELECT * FROM %s WHERE b = 0"), row(0, 0, 2, 0));
+ assertRows(execute("SELECT * FROM %s WHERE d = 4"), row(1, 1, 3, 4));
+
+ execute("DELETE FROM %s WHERE a = 0");
+ execute("DELETE FROM %s WHERE a = 1 AND b = 1");
+ assertEmpty(execute("SELECT * FROM %s WHERE b = 0"));
+ assertEmpty(execute("SELECT * FROM %s WHERE d = 3"));
+ }
+
++ @Test
+ public void testWithEmptyRestrictionValueAndSecondaryIndex() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))");
+ createIndex("CREATE INDEX on %s(c)");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)", bytes("foo123"), bytes("2"), bytes("1"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
-
++ beforeAndAfterFlush(() -> {
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob('')) AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
- }
++ });
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"));
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob('')) AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob('')) AND c < textAsBlob('') AND v = textAsBlob('1') ALLOW FILTERING;"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
- }
++ });
+
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER);
+
- for (boolean flush : new boolean[]{false, true})
- {
- if (flush)
- flush();
++ beforeAndAfterFlush(() -> {
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"),
+ row(bytes("foo123"), bytes("3"), EMPTY_BYTE_BUFFER));
- }
++ });
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithSecondaryIndexAndCompactTables() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c)) WITH COMPACT STORAGE");
- assertInvalidMessage("Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables",
++ assertInvalidMessage("Secondary indexes are not supported on COMPACT STORAGE tables that have clustering columns",
+ "CREATE INDEX on %s(c)");
+
+ createTable("CREATE TABLE %s (pk blob PRIMARY KEY, v blob) WITH COMPACT STORAGE");
+ createIndex("CREATE INDEX on %s(v)");
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo123"), bytes("1"));
+
+ // Test restrictions on non-primary key value
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('');"));
+
+ execute("INSERT INTO %s (pk, v) VALUES (?, ?)", bytes("foo124"), EMPTY_BYTE_BUFFER);
+
+ assertRows(execute("SELECT * FROM %s WHERE v = textAsBlob('');"),
+ row(bytes("foo124"), EMPTY_BYTE_BUFFER));
+ }
+
- /**
- * Custom index used to test the behavior of the system when the index is not ready.
- * As Custom indices cannot by <code>PerColumnSecondaryIndex</code> we use a <code>PerRowSecondaryIndex</code>
- * to avoid the check but return a <code>CompositesSearcher</code>.
- */
- public static class IndexBlockingOnInitialization extends PerRowSecondaryIndex
+ private ResultMessage.Prepared prepareStatement(String cql, boolean forThrift)
{
- private volatile CountDownLatch latch = new CountDownLatch(1);
-
- @Override
- public void index(ByteBuffer rowKey, ColumnFamily cf)
- {
- try
- {
- latch.await();
- }
- catch (InterruptedException e)
- {
- Thread.interrupted();
- }
- }
-
- @Override
- public void delete(DecoratedKey key, Group opGroup)
- {
- }
+ return QueryProcessor.prepare(String.format(cql, KEYSPACE, currentTable()),
+ ClientState.forInternalCalls(),
+ forThrift);
+ }
- @Override
- public void init()
- {
- }
+ private void validateCell(Cell cell, ColumnDefinition def, ByteBuffer val, long timestamp)
+ {
+ assertNotNull(cell);
+ assertEquals(0, def.type.compare(cell.value(), val));
+ assertEquals(timestamp, cell.timestamp());
+ }
- @Override
- public void reload()
- {
- }
+ private static void assertColumnValue(int expected, String name, Row row, CFMetaData cfm)
+ {
+ ColumnDefinition col = cfm.getColumnDefinition(new ColumnIdentifier(name, true));
+ AbstractType<?> type = col.type;
+ assertEquals(expected, type.compose(row.getCell(col).value()));
+ }
- @Override
- public void validateOptions() throws ConfigurationException
- {
- }
+ /**
+ * <code>CassandraIndex</code> that blocks during the initialization.
+ */
+ public static class IndexBlockingOnInitialization extends CustomCassandraIndex
+ {
+ private final CountDownLatch latch = new CountDownLatch(1);
- @Override
- public String getIndexName()
+ public IndexBlockingOnInitialization(ColumnFamilyStore baseCfs, IndexMetadata indexDef)
{
- return "testIndex";
+ super(baseCfs, indexDef);
}
@Override
[02/12] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by bl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index c8df4c3,cef4635..ac1ba4c
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@@ -23,15 -23,18 +23,18 @@@ import java.util.UUID
import org.junit.Test;
import junit.framework.Assert;
-import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.UntypedResultSet;
-import org.apache.cassandra.cql3.CQLTester;
-import org.apache.cassandra.dht.Murmur3Partitioner;
+import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.exceptions.InvalidRequestException;
- import org.apache.cassandra.utils.ByteBufferUtil;
+
+import org.apache.cassandra.cql3.CQLTester;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+
+ import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+
/**
* Test column ranges and ordering with static column in table
*/
@@@ -1367,891 -1240,6 +1370,891 @@@ public class SelectTest extends CQLTest
}
@Test
+ public void testAlias() throws Throwable
+ {
+ createTable("CREATE TABLE %s (id int PRIMARY KEY, name text)");
+
+ for (int i = 0; i < 5; i++)
+ execute("INSERT INTO %s (id, name) VALUES (?, ?) USING TTL 10 AND TIMESTAMP 0", i, Integer.toString(i));
+
+ assertInvalidMessage("Aliases aren't allowed in the where clause",
+ "SELECT id AS user_id, name AS user_name FROM %s WHERE user_id = 0");
+
+ // test that select throws a meaningful exception for aliases in order by clause
+ assertInvalidMessage("Aliases are not allowed in order by clause",
+ "SELECT id AS user_id, name AS user_name FROM %s WHERE id IN (0) ORDER BY user_name");
+
+ }
+
+ @Test
+ public void testFilteringWithoutIndices() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c int, d int, e map<int, int>, PRIMARY KEY (a, b))");
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c, d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = 1 AND d = 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 1 AND c = 2 ALLOW FILTERING");
+ assertInvalidMessage("IN predicates on non-primary-key columns (c) is not yet supported",
+ "SELECT * FROM %s WHERE a IN (1, 2) AND c IN (2, 3) ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS 1 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY 1 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY ? ALLOW FILTERING", unset());
+
+ createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY(a)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 2, 8)");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 6, 4)");
+
+ assertRows(execute("SELECT * FROM %s WHERE c = 4 ALLOW FILTERING"),
+ row(1, 2, 4),
+ row(3, 6, 4));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c", "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Unsupported null value for column c", "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = ?", unset());
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > ?", unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING", unset());
+ }
+
+ @Test
+ public void testFilteringOnStaticColumnWithoutIndices() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, s int static, c int, PRIMARY KEY (a, b))");
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c, s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = 1 AND s = 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 1 AND s = 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s > 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s > null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s = ? ALLOW FILTERING", unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (s) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE s > ? ALLOW FILTERING", unset());
+ }
+
+ @Test
+ public void testFilteringOnCompactTablesWithoutIndices() throws Throwable
+ {
+ //----------------------------------------------
+ // Test COMPACT table with clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY (a, b)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 3, 6)");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 4, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 3, 7)");
+
+ // Lets add some tombstones to make sure that filtering handle them properly
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 1, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 2, 7)");
+ execute("DELETE FROM %s WHERE a = 1 AND b = 1");
+ execute("DELETE FROM %s WHERE a = 2 AND b = 2");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = 4 ALLOW FILTERING");
+
+ assertInvalidMessage("IN predicates on non-primary-key columns (c) is not yet supported",
+ "SELECT * FROM %s WHERE a IN (1, 2) AND c IN (6, 7) ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > 4 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+
+ //----------------------------------------------
+ // Test COMPACT table without clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c int) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 1, 6)");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (4, 1, 7)");
+
+ // Lets add some tombstones to make sure that filtering handle them properly
+ execute("INSERT INTO %s (a, b, c) VALUES (0, 1, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES (5, 2, 7)");
+ execute("DELETE FROM %s WHERE a = 0");
+ execute("DELETE FROM %s WHERE a = 5");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = 4");
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = 4 ALLOW FILTERING"),
+ row(1, 2, 4));
+
+ assertInvalidMessage("IN predicates on non-primary-key columns (c) is not yet supported",
+ "SELECT * FROM %s WHERE a IN (1, 2) AND c IN (6, 7)");
+
+ assertInvalidMessage("IN predicates on non-primary-key columns (c) is not yet supported",
+ "SELECT * FROM %s WHERE a IN (1, 2) AND c IN (6, 7) ALLOW FILTERING");
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > 4");
+
+ assertRows(execute("SELECT * FROM %s WHERE c > 4 ALLOW FILTERING"),
+ row(2, 1, 6),
+ row(4, 1, 7));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE b < 3 AND c <= 4");
+
+ assertRows(execute("SELECT * FROM %s WHERE b < 3 AND c <= 4 ALLOW FILTERING"),
+ row(1, 2, 4),
+ row(3, 2, 4));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c >= 3 AND c <= 6");
+
+ assertRows(execute("SELECT * FROM %s WHERE c >= 3 AND c <= 6 ALLOW FILTERING"),
+ row(1, 2, 4),
+ row(2, 1, 6),
+ row(3, 2, 4));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testFilteringWithoutIndicesWithCollections() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c list<int>, d set<int>, e map<int, int>, PRIMARY KEY (a, b))");
+
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 2, [1, 6], {2, 12}, {1: 6})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 3, [3, 2], {6, 4}, {3: 2})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 4, [1, 2], {2, 4}, {1: 2})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (2, 3, [3, 6], {6, 12}, {3: 6})");
+
+ flush();
+
+ // Checks filtering for lists
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ // Checks filtering for sets
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS 4 ALLOW FILTERING");
+
+ // Checks filtering for maps
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS 2 ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e[null] = 2 ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e[1] = null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e[?] = 2 ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e[1] = ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testFilteringWithoutIndicesWithFrozenCollections() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c frozen<list<int>>, d frozen<set<int>>, e frozen<map<int, int>>, PRIMARY KEY (a, b))");
+
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 2, [1, 6], {2, 12}, {1: 6})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 3, [3, 2], {6, 4}, {3: 2})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 4, [1, 2], {2, 4}, {1: 2})");
+ execute("INSERT INTO %s (a, b, c, d, e) VALUES (2, 3, [3, 6], {6, 12}, {3: 6})");
+
+ flush();
+
+ // Checks filtering for lists
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = [3, 2] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > [1, 5] AND c < [3, 6] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ // Checks filtering for sets
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d = {6, 4} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d > {4, 5} AND d < {6} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS 4 ALLOW FILTERING");
+
+ // Checks filtering for maps
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e = {1 : 2} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e > {1 : 4} AND e < {3 : 6} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS 2 ALLOW FILTERING");
+
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[1] = 6 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY null ALLOW FILTERING");
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[null] = 2 ALLOW FILTERING");
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[1] = null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (d) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE d CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (e) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE e CONTAINS KEY ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[?] = 2 ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Map-entry equality predicates on frozen map column e are not supported",
+ "SELECT * FROM %s WHERE e[1] = ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testFilteringOnCompactTablesWithoutIndicesAndWithLists() throws Throwable
+ {
+ //----------------------------------------------
+ // Test COMPACT table with clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int, b int, c frozen<list<int>>, PRIMARY KEY (a, b)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, [4, 2])");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 3, [6, 2])");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 4, [4, 1])");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 3, [7, 1])");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = [4, 1] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > [4, 2] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE b <= 3 AND c < [6, 2] ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+
+ //----------------------------------------------
+ // Test COMPACT table without clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c frozen<list<int>>) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, [4, 2])");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 1, [6, 2])");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 2, [4, 1])");
+ execute("INSERT INTO %s (a, b, c) VALUES (4, 1, [7, 1])");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = [4, 2]");
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = [4, 2] ALLOW FILTERING"),
+ row(1, 2, list(4, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > [4, 2]");
+
+ assertRows(execute("SELECT * FROM %s WHERE c > [4, 2] ALLOW FILTERING"),
+ row(2, 1, list(6, 2)),
+ row(4, 1, list(7, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE b < 3 AND c <= [4, 2]");
+
+ assertRows(execute("SELECT * FROM %s WHERE b < 3 AND c <= [4, 2] ALLOW FILTERING"),
+ row(1, 2, list(4, 2)),
+ row(3, 2, list(4, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c >= [4, 3] AND c <= [7]");
+
+ assertRows(execute("SELECT * FROM %s WHERE c >= [4, 3] AND c <= [7] ALLOW FILTERING"),
+ row(2, 1, list(6, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS 2");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING"),
+ row(1, 2, list(4, 2)),
+ row(2, 1, list(6, 2)));
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY 2 ALLOW FILTERING");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 AND c CONTAINS 6 ALLOW FILTERING"),
+ row(2, 1, list(6, 2)));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testFilteringOnCompactTablesWithoutIndicesAndWithSets() throws Throwable
+ {
+ //----------------------------------------------
+ // Test COMPACT table with clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int, b int, c frozen<set<int>>, PRIMARY KEY (a, b)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4, 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 3, {6, 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 4, {4, 1})");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 3, {7, 1})");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = {4, 1} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > {4, 2} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c >= {4, 2} AND c <= {6, 4} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+
+ //----------------------------------------------
+ // Test COMPACT table without clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c frozen<set<int>>) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4, 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 1, {6, 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 2, {4, 1})");
+ execute("INSERT INTO %s (a, b, c) VALUES (4, 1, {7, 1})");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = {4, 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = {4, 2} ALLOW FILTERING"),
+ row(1, 2, set(4, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > {4, 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE c > {4, 2} ALLOW FILTERING"),
+ row(2, 1, set(6, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE b < 3 AND c <= {4, 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE b < 3 AND c <= {4, 2} ALLOW FILTERING"),
+ row(1, 2, set(4, 2)),
+ row(4, 1, set(1, 7)),
+ row(3, 2, set(4, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c >= {4, 3} AND c <= {7}");
+
+ assertRows(execute("SELECT * FROM %s WHERE c >= {5, 2} AND c <= {7} ALLOW FILTERING"),
+ row(2, 1, set(6, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS 2");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING"),
+ row(1, 2, set(4, 2)),
+ row(2, 1, set(6, 2)));
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY 2 ALLOW FILTERING");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 AND c CONTAINS 6 ALLOW FILTERING"),
+ row(2, 1, set(6, 2)));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ }
+
+ @Test
+ public void testIndexQueryWithValueOver64K() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c blob, PRIMARY KEY (a, b))");
+ createIndex("CREATE INDEX test ON %s (c)");
+
- execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 0, ByteBufferUtil.bytes(1));
- execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, ByteBufferUtil.bytes(2));
++ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 0, bytes(1));
++ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, 1, bytes(2));
+
+ assertInvalidMessage("Index expression values may not be larger than 64K",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING", TOO_BIG);
+ }
+
+ @Test
+ public void testFilteringOnCompactTablesWithoutIndicesAndWithMaps() throws Throwable
+ {
+ //----------------------------------------------
+ // Test COMPACT table with clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int, b int, c frozen<map<int, int>>, PRIMARY KEY (a, b)) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4 : 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 3, {6 : 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 4, {4 : 1})");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 3, {7 : 1})");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = {4 : 1} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > {4 : 2} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE b <= 3 AND c < {6 : 2} ALLOW FILTERING");
+
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING");
+
+ // Checks filtering with null
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS KEY null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Predicates on non-primary-key columns (c) are not yet supported for non secondary index queries",
+ "SELECT * FROM %s WHERE c CONTAINS KEY ? ALLOW FILTERING",
+ unset());
+
+ //----------------------------------------------
+ // Test COMPACT table without clustering columns
+ //----------------------------------------------
+ createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c frozen<map<int, int>>) WITH COMPACT STORAGE");
+
+ execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4 : 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (2, 1, {6 : 2})");
+ execute("INSERT INTO %s (a, b, c) VALUES (3, 2, {4 : 1})");
+ execute("INSERT INTO %s (a, b, c) VALUES (4, 1, {7 : 1})");
+
+ flush();
+
+ // Checks filtering
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = {4 : 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = {4 : 2} ALLOW FILTERING"),
+ row(1, 2, map(4, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > {4 : 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE c > {4 : 2} ALLOW FILTERING"),
+ row(2, 1, map(6, 2)),
+ row(4, 1, map(7, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE b < 3 AND c <= {4 : 2}");
+
+ assertRows(execute("SELECT * FROM %s WHERE b < 3 AND c <= {4 : 2} ALLOW FILTERING"),
+ row(1, 2, map(4, 2)),
+ row(3, 2, map(4, 1)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c >= {4 : 3} AND c <= {7 : 1}");
+
+ assertRows(execute("SELECT * FROM %s WHERE c >= {5 : 2} AND c <= {7 : 0} ALLOW FILTERING"),
+ row(2, 1, map(6, 2)));
+
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS 2");
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 ALLOW FILTERING"),
+ row(1, 2, map(4, 2)),
+ row(2, 1, map(6, 2)));
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS KEY 4 ALLOW FILTERING"),
+ row(1, 2, map(4, 2)),
+ row(3, 2, map(4, 1)));
+
+ assertRows(execute("SELECT * FROM %s WHERE c CONTAINS 2 AND c CONTAINS KEY 6 ALLOW FILTERING"),
+ row(2, 1, map(6, 2)));
+
+ // Checks filtering with null
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c = null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c = null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c > null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c > null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS null ALLOW FILTERING");
+ assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+ "SELECT * FROM %s WHERE c CONTAINS KEY null");
+ assertInvalidMessage("Unsupported null value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY null ALLOW FILTERING");
+
+ // Checks filtering with unset
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c = ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c > ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS ? ALLOW FILTERING",
+ unset());
+ assertInvalidMessage("Unsupported unset value for column c",
+ "SELECT * FROM %s WHERE c CONTAINS KEY ? ALLOW FILTERING",
+ unset());
+ }
+
+ /**
+ * Check select with and without compact storage, with different column
+ * order. See CASSANDRA-10988
+ */
+ @Test
+ public void testClusteringOrderWithSlice() throws Throwable
+ {
+ for (String compactOption : new String[] { "", " COMPACT STORAGE AND" })
+ {
+ // non-compound, ASC order
+ createTable("CREATE TABLE %s (a text, b int, PRIMARY KEY (a, b)) WITH" +
+ compactOption +
+ " CLUSTERING ORDER BY (b ASC)");
+
+ execute("INSERT INTO %s (a, b) VALUES ('a', 2)");
+ execute("INSERT INTO %s (a, b) VALUES ('a', 3)");
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0"),
+ row("a", 2),
+ row("a", 3));
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0 ORDER BY b DESC"),
+ row("a", 3),
+ row("a", 2));
+
+ // non-compound, DESC order
+ createTable("CREATE TABLE %s (a text, b int, PRIMARY KEY (a, b)) WITH" +
+ compactOption +
+ " CLUSTERING ORDER BY (b DESC)");
+
+ execute("INSERT INTO %s (a, b) VALUES ('a', 2)");
+ execute("INSERT INTO %s (a, b) VALUES ('a', 3)");
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0"),
+ row("a", 3),
+ row("a", 2));
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0 ORDER BY b ASC"),
+ row("a", 2),
+ row("a", 3));
+
+ // compound, first column DESC order
+ createTable("CREATE TABLE %s (a text, b int, c int, PRIMARY KEY (a, b, c)) WITH" +
+ compactOption +
+ " CLUSTERING ORDER BY (b DESC)"
+ );
+
+ execute("INSERT INTO %s (a, b, c) VALUES ('a', 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES ('a', 3, 5)");
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0"),
+ row("a", 3, 5),
+ row("a", 2, 4));
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0 ORDER BY b ASC"),
+ row("a", 2, 4),
+ row("a", 3, 5));
+
+ // compound, mixed order
+ createTable("CREATE TABLE %s (a text, b int, c int, PRIMARY KEY (a, b, c)) WITH" +
+ compactOption +
+ " CLUSTERING ORDER BY (b ASC, c DESC)"
+ );
+
+ execute("INSERT INTO %s (a, b, c) VALUES ('a', 2, 4)");
+ execute("INSERT INTO %s (a, b, c) VALUES ('a', 3, 5)");
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0"),
+ row("a", 2, 4),
+ row("a", 3, 5));
+
+ assertRows(execute("SELECT * FROM %s WHERE a = 'a' AND b > 0 ORDER BY b ASC"),
+ row("a", 2, 4),
+ row("a", 3, 5));
+ }
+ }
+
+ @Test
public void testOverlyLargeSelectPK() throws Throwable
{
createTable("CREATE TABLE %s (a text, b text, PRIMARY KEY ((a), b))");
@@@ -2339,4 -1330,375 +2342,367 @@@
row(1, 2, 3),
row(1, 1, 3));
}
+
+ @Test
+ public void testEmptyRestrictionValue() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk = textAsBlob('');");
+ assertInvalidMessage("Key may not be empty", "SELECT * FROM %s WHERE pk IN (textAsBlob(''), textAsBlob('1'));");
+
+ assertInvalidMessage("Key may not be empty",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ EMPTY_BYTE_BUFFER, bytes("2"), bytes("2"));
+
+ // Test clustering columns restrictions
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('') AND c < textAsBlob('');"));
+ }
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Missing PRIMARY KEY part c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
-
- // Test restrictions on non-primary key value
- assertInvalidMessage("Predicates on the non-primary-key column (v) of a COMPACT table are not yet supported",
- "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) = (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) IN ((textAsBlob('')), (textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) > (textAsBlob(''));"),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) >= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) <= (textAsBlob(''));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c) < (textAsBlob(''));"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('') AND c < textAsBlob('');"));
-
- // Test restrictions on non-primary key value
- assertInvalidMessage("No secondary indexes on the restricted columns support the provided operators",
- "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ }
++
++ // Test restrictions on non-primary key value
++ assertInvalidMessage("Predicates on non-primary-key columns (v) are not yet supported for non secondary index queries",
++ "SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND v = textAsBlob('') ALLOW FILTERING;");
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumns() throws Throwable
+ {
+ for (String options : new String[] { "", " WITH COMPACT STORAGE" })
+ {
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 = textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob('1'), textAsBlob(''));"));
+
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
- assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"));
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
++ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('');"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 <= textAsBlob('');"));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob('1'), textAsBlob(''));"));
+ }
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 = textAsBlob('1');"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) = (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
- row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
-
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'));"),
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1');"),
++ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) <= (textAsBlob(''), textAsBlob('1'));"),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) < (textAsBlob(''), textAsBlob('1'));"));
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c blob, v blob, PRIMARY KEY ((pk), c))" + options);
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("1"),
+ bytes("1"));
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ bytes("2"),
+ bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause));
-
+ }
+
+ if (options.contains("COMPACT"))
+ {
+ assertInvalidMessage("Missing PRIMARY KEY part c",
+ "INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"),
+ EMPTY_BYTE_BUFFER,
+ bytes("4"));
+ }
+ else
+ {
+ execute("INSERT INTO %s (pk, c, v) VALUES (?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+
+ assertEmpty(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c < textAsBlob('')" + orderingClause));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c <= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("4")));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testEmptyRestrictionValueWithMultipleClusteringColumnsAndOrderBy() throws Throwable
+ {
+ for (String options : new String[] { "",
+ " WITH COMPACT STORAGE",
+ " WITH CLUSTERING ORDER BY (c1 DESC, c2 DESC)",
+ " WITH COMPACT STORAGE AND CLUSTERING ORDER BY (c1 DESC, c2 DESC)"})
+ {
+ String orderingClause = options.contains("ORDER") ? "" : "ORDER BY c1 DESC, c2 DESC" ;
+
+ createTable("CREATE TABLE %s (pk blob, c1 blob, c2 blob, v blob, PRIMARY KEY (pk, c1, c2))" + options);
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("1"), bytes("1"));
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)", bytes("foo123"), bytes("1"), bytes("2"), bytes("2"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 > textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 >= textAsBlob('')" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+ }
+
+ execute("INSERT INTO %s (pk, c1, c2, v) VALUES (?, ?, ?, ?)",
+ bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4"));
+
+ for (boolean flush : new boolean[]{false, true})
+ {
+ if (flush)
+ flush();
+
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('1') AND c2 IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
- row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
-
- assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 = textAsBlob('') AND c2 IN (textAsBlob(''), textAsBlob('1'))" + orderingClause),
++ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND c1 IN (textAsBlob(''), textAsBlob('1')) AND c2 = textAsBlob('1')" + orderingClause),
++ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) IN ((textAsBlob(''), textAsBlob('1')), (textAsBlob('1'), textAsBlob('1')))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) > (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")));
+
+ assertRows(execute("SELECT * FROM %s WHERE pk = textAsBlob('foo123') AND (c1, c2) >= (textAsBlob(''), textAsBlob('1'))" + orderingClause),
+ row(bytes("foo123"), bytes("1"), bytes("2"), bytes("2")),
+ row(bytes("foo123"), bytes("1"), bytes("1"), bytes("1")),
+ row(bytes("foo123"), EMPTY_BYTE_BUFFER, bytes("1"), bytes("4")));
+ }
+ }
+ }
}
[05/12] cassandra git commit: Merge branch cassandra-2.1 into
cassandra-2.2
Posted by bl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 8820ff7,40f3f33..edf8e47
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@@ -181,8 -247,11 +181,11 @@@ public class SelectStatement implement
private Pageable getPageableCommand(QueryOptions options, int limit, long now) throws RequestValidationException
{
- if (isNotReturningAnyRows(options))
++ if (restrictions.isNotReturningAnyRows(options))
+ return null;
+
int limitForQuery = updateLimitForQuery(limit);
- if (isKeyRange || usesSecondaryIndexing)
+ if (restrictions.isKeyRange() || restrictions.usesSecondaryIndexing())
return getRangeCommand(options, limitForQuery, now);
List<ReadCommand> commands = getSliceCommands(options, limitForQuery, now);
@@@ -596,52 -961,338 +599,58 @@@
}
}
- /** Returns true if a non-frozen collection is selected, false otherwise. */
- private boolean selectACollection()
+ /**
+ * May be used by custom QueryHandler implementations
+ */
+ public List<IndexExpression> getValidatedIndexExpressions(QueryOptions options) throws InvalidRequestException
{
- if (!cfm.comparator.hasCollections())
- return false;
+ if (!restrictions.usesSecondaryIndexing())
+ return Collections.emptyList();
- for (ColumnDefinition def : selection.getColumns())
- {
- if (def.type.isCollection() && def.type.isMultiCell())
- return true;
- }
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(columnFamily());
+ SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
- return false;
+ List<IndexExpression> expressions = restrictions.getIndexExpressions(secondaryIndexManager, options);
+ secondaryIndexManager.validateIndexSearchersForQuery(expressions);
+
+ return expressions;
}
- @VisibleForTesting
- static List<Composite> buildBound(Bound bound,
- List<ColumnDefinition> defs,
- Restriction[] restrictions,
- boolean isReversed,
- CType type,
- QueryOptions options) throws InvalidRequestException
+ private CellName makeExclusiveSliceBound(Bound bound, CellNameType type, QueryOptions options) throws InvalidRequestException
{
- CBuilder builder = type.builder();
-
- // The end-of-component of composite doesn't depend on whether the
- // component type is reversed or not (i.e. the ReversedType is applied
- // to the component comparator but not to the end-of-component itself),
- // it only depends on whether the slice is reversed
- Bound eocBound = isReversed ? Bound.reverse(bound) : bound;
- for (int i = 0, m = defs.size(); i < m; i++)
- {
- ColumnDefinition def = defs.get(i);
-
- // In a restriction, we always have Bound.START < Bound.END for the "base" comparator.
- // So if we're doing a reverse slice, we must inverse the bounds when giving them as start and end of the slice filter.
- // But if the actual comparator itself is reversed, we must inversed the bounds too.
- Bound b = isReversed == isReversedType(def) ? bound : Bound.reverse(bound);
- Restriction r = restrictions[def.position()];
- if (isNullRestriction(r, b) || !r.canEvaluateWithSlices())
- {
- // There wasn't any non EQ relation on that key, we select all records having the preceding component as prefix.
- // For composites, if there was preceding component and we're computing the end, we must change the last component
- // End-Of-Component, otherwise we would be selecting only one record.
- Composite prefix = builder.build();
- return Collections.singletonList(eocBound == Bound.END ? prefix.end() : prefix.start());
- }
- if (r.isSlice())
- {
- if (r.isMultiColumn())
- {
- MultiColumnRestriction.Slice slice = (MultiColumnRestriction.Slice) r;
+ // clusteringColumnBounds may reverse bound if clustering order is reversed
+ // but areRequestedBoundsInclusive checks for Restriction::isInclusive and never
+ // reverses the order. In order to avoid inconsistencies and check inclusive
+ // bounds correctly, we need to check for column order and reverse it. See CASSANDRA-10988
+ if (restrictions.areRequestedBoundsInclusive(reverseBoundIfNeeded(bound)))
+ return null;
- return type.makeCellName(restrictions.getClusteringColumnsBounds(bound, options).get(0));
- if (!slice.hasBound(b))
- {
- Composite prefix = builder.build();
- return Collections.singletonList(builder.remainingCount() > 0 && eocBound == Bound.END
- ? prefix.end()
- : prefix);
- }
++ // We can only reach that if the table is dense non-compound tables.
++ // By consequence, we know that the table is a COMPACT table with only one clustering column.
++ ByteBuffer value = restrictions.getClusteringColumnsBounds(bound, options).get(0);
+
- List<ByteBuffer> vals = slice.componentBounds(b, options);
-
- for (int j = 0, n = vals.size(); j < n; j++)
- addValue(builder, defs.get(i + j), vals.get(j)) ;
- }
- else
- {
- builder.add(getSliceValue(r, b, options));
- }
- Operator relType = ((Restriction.Slice)r).getRelation(eocBound, b);
- return Collections.singletonList(builder.build().withEOC(eocForRelation(relType)));
- }
-
- if (r.isIN())
- {
- // The IN query might not have listed the values in comparator order, so we need to re-sort
- // the bounds lists to make sure the slices works correctly (also, to avoid duplicates).
- TreeSet<Composite> inValues = new TreeSet<>(isReversed ? type.reverseComparator() : type);
-
- if (r.isMultiColumn())
- {
- List<List<ByteBuffer>> splitInValues = ((MultiColumnRestriction.IN) r).splitValues(options);
-
- for (List<ByteBuffer> components : splitInValues)
- {
- for (int j = 0; j < components.size(); j++)
- if (components.get(j) == null)
- throw new InvalidRequestException("Invalid null value in condition for column " + defs.get(i + j).name);
-
- Composite prefix = builder.buildWith(components);
- inValues.add(builder.remainingCount() == 0 ? prefix : addEOC(prefix, eocBound));
- }
- return new ArrayList<>(inValues);
- }
-
- List<ByteBuffer> values = r.values(options);
- if (values.size() != 1)
- {
- // IN query, we only support it on the clustering columns
- assert def.position() == defs.size() - 1;
- for (ByteBuffer val : values)
- {
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value in condition for column %s",
- def.name));
- Composite prefix = builder.buildWith(val);
- // See below for why this
- inValues.add(builder.remainingCount() == 0 ? prefix : addEOC(prefix, eocBound));
- }
- return new ArrayList<>(inValues);
- }
- }
-
- List<ByteBuffer> values = r.values(options);
-
- if (r.isMultiColumn())
- {
- for (int j = 0; j < values.size(); j++)
- addValue(builder, defs.get(i + j), values.get(j));
- i += values.size() - 1; // skips the processed columns
- }
- else
- {
- addValue(builder, def, values.get(0));
- }
- }
- // Means no relation at all or everything was an equal
- // Note: if the builder is "full", there is no need to use the end-of-component bit. For columns selection,
- // it would be harmless to do it. However, we use this method got the partition key too. And when a query
- // with 2ndary index is done, and with the the partition provided with an EQ, we'll end up here, and in that
- // case using the eoc would be bad, since for the random partitioner we have no guarantee that
- // prefix.end() will sort after prefix (see #5240).
- Composite prefix = builder.build();
- return Collections.singletonList(builder.remainingCount() == 0 ? prefix : addEOC(prefix, eocBound));
- }
++ // Dense non-compound tables do not accept empty ByteBuffers. By consequence, if the slice value is empty
++ // we know that we can treat the slice as inclusive.
++ return value.hasRemaining() ? type.makeCellName(value) : null;
+ }
/**
- * Adds an EOC to the specified Composite.
- *
- * @param composite the composite
- * @param eocBound the EOC bound
- * @return a new <code>Composite</code> with the EOC corresponding to the eocBound
+ * Reverses the specified bound if the non-compound clustering column is a reversed one.
+ * @param bound bound to reverse
+ * @return the bound reversed if the column type was a reversed one or the original bound
*/
- private static Composite addEOC(Composite composite, Bound eocBound)
+ private Bound reverseBoundIfNeeded(Bound bound)
{
- return eocBound == Bound.END ? composite.end() : composite.start();
- }
+ assert !cfm.comparator.isCompound();
- /**
- * Adds the specified value to the specified builder
- *
- * @param builder the CBuilder to which the value must be added
- * @param def the column associated to the value
- * @param value the value to add
- * @throws InvalidRequestException if the value is null
- */
- private static void addValue(CBuilder builder, ColumnDefinition def, ByteBuffer value) throws InvalidRequestException
- {
- if (value == null)
- throw new InvalidRequestException(String.format("Invalid null value in condition for column %s", def.name));
- builder.add(value);
- }
-
- private static Composite.EOC eocForRelation(Operator op)
- {
- switch (op)
- {
- case LT:
- // < X => using startOf(X) as finish bound
- return Composite.EOC.START;
- case GT:
- case LTE:
- // > X => using endOf(X) as start bound
- // <= X => using endOf(X) as finish bound
- return Composite.EOC.END;
- default:
- // >= X => using X as start bound (could use START_OF too)
- // = X => using X
- return Composite.EOC.NONE;
- }
- }
-
- private static boolean isNullRestriction(Restriction r, Bound b)
- {
- return r == null || (r.isSlice() && !((Restriction.Slice)r).hasBound(b));
- }
-
- private static ByteBuffer getSliceValue(Restriction r, Bound b, QueryOptions options) throws InvalidRequestException
- {
- Restriction.Slice slice = (Restriction.Slice)r;
- assert slice.hasBound(b);
- ByteBuffer val = slice.bound(b, options);
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null clustering key part %s", r));
- return val;
- }
-
- private List<Composite> getRequestedBound(Bound b, QueryOptions options) throws InvalidRequestException
- {
- assert isColumnRange();
- List<Composite> bound = buildBound(b, cfm.clusteringColumns(), columnRestrictions, isReversed, cfm.comparator, options);
- for (Composite c : bound) {
- if (!c.isEmpty())
- QueryProcessor.validateComposite(c, cfm.comparator);
- }
- return bound;
+ List<ColumnDefinition> columnDefs = cfm.clusteringColumns();
+ return columnDefs.get(columnDefs.size() - 1).isReversedType() ? bound.reverse() : bound;
}
- /**
- * May be used by custom QueryHandler implementations
- */
- public List<IndexExpression> getValidatedIndexExpressions(QueryOptions options) throws InvalidRequestException
- {
- if (!usesSecondaryIndexing || restrictedColumns.isEmpty())
- return Collections.emptyList();
-
- List<IndexExpression> expressions = new ArrayList<IndexExpression>();
- for (ColumnDefinition def : restrictedColumns.keySet())
- {
- Restriction restriction;
- switch (def.kind)
- {
- case PARTITION_KEY:
- restriction = keyRestrictions[def.position()];
- break;
- case CLUSTERING_COLUMN:
- restriction = columnRestrictions[def.position()];
- break;
- case REGULAR:
- case STATIC:
- restriction = metadataRestrictions.get(def.name);
- break;
- default:
- // We don't allow restricting a COMPACT_VALUE for now in prepare.
- throw new AssertionError();
- }
-
- if (restriction.isSlice())
- {
- Restriction.Slice slice = (Restriction.Slice)restriction;
- for (Bound b : Bound.values())
- {
- if (slice.hasBound(b))
- {
- ByteBuffer value = validateIndexedValue(def, slice.bound(b, options));
- Operator op = slice.getIndexOperator(b);
- // If the underlying comparator for name is reversed, we need to reverse the IndexOperator: user operation
- // always refer to the "forward" sorting even if the clustering order is reversed, but the 2ndary code does
- // use the underlying comparator as is.
- if (def.type instanceof ReversedType)
- op = reverse(op);
- expressions.add(new IndexExpression(def.name.bytes, op, value));
- }
- }
- }
- else if (restriction.isContains())
- {
- SingleColumnRestriction.Contains contains = (SingleColumnRestriction.Contains)restriction;
- for (ByteBuffer value : contains.values(options))
- {
- validateIndexedValue(def, value);
- expressions.add(new IndexExpression(def.name.bytes, Operator.CONTAINS, value));
- }
- for (ByteBuffer key : contains.keys(options))
- {
- validateIndexedValue(def, key);
- expressions.add(new IndexExpression(def.name.bytes, Operator.CONTAINS_KEY, key));
- }
- }
- else
- {
- ByteBuffer value;
- if (restriction.isMultiColumn())
- {
- List<ByteBuffer> values = restriction.values(options);
- value = values.get(def.position());
- }
- else
- {
- List<ByteBuffer> values = restriction.values(options);
- if (values.size() != 1)
- throw new InvalidRequestException("IN restrictions are not supported on indexed columns");
-
- value = values.get(0);
- }
-
- validateIndexedValue(def, value);
- expressions.add(new IndexExpression(def.name.bytes, Operator.EQ, value));
- }
- }
-
- if (usesSecondaryIndexing)
- {
- ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(columnFamily());
- SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
- secondaryIndexManager.validateIndexSearchersForQuery(expressions);
- }
-
- return expressions;
- }
-
- private static ByteBuffer validateIndexedValue(ColumnDefinition def, ByteBuffer value) throws InvalidRequestException
- {
- if (value == null)
- throw new InvalidRequestException(String.format("Unsupported null value for indexed column %s", def.name));
- if (value.remaining() > 0xFFFF)
- throw new InvalidRequestException("Index expression values may not be larger than 64K");
- return value;
- }
-
- private CellName makeExclusiveSliceBound(Bound bound, CellNameType type, QueryOptions options) throws InvalidRequestException
- {
- if (sliceRestriction.isInclusive(bound))
- return null;
-
- // We can only reach that point if cfm.comparator.isCompound() = false and the table has some clustering columns.
- // By consequence, we know that the table is a COMPACT table with only one clustering column.
- ByteBuffer value = sliceRestriction.isMultiColumn() ? ((MultiColumnRestriction.Slice) sliceRestriction).componentBounds(bound, options).get(0)
- : sliceRestriction.bound(bound, options);
-
- // Dense non-compound tables do not accept empty ByteBuffers. By consequence, if the slice value is empty
- // we know that we can treat the slice as inclusive.
- return value.hasRemaining() ? type.makeCellName(value) : null;
- }
-
-
private Iterator<Cell> applySliceRestriction(final Iterator<Cell> cells, final QueryOptions options) throws InvalidRequestException
{
- assert sliceRestriction != null;
-
final CellNameType type = cfm.comparator;
+
final CellName excludedStart = makeExclusiveSliceBound(Bound.START, type, options);
final CellName excludedEnd = makeExclusiveSliceBound(Bound.END, type, options);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
index 91a059f,39e632a..517d842
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@@ -86,7 -84,7 +86,7 @@@ public class UpdateStatement extends Mo
if (cfm.comparator.isDense())
{
if (prefix.isEmpty())
- throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().get(0)));
- throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().iterator().next().name));
++ throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().get(0).name));
// An empty name for the compact value is what we use to recognize the case where there is not column
// outside the PK, see CreateStatement.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/compaction/Scrubber.java
index d1fe702,2df3665..99ee62e
--- a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
+++ b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
@@@ -23,12 -23,12 +23,14 @@@ import java.util.*
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
+ import com.google.common.collect.AbstractIterator;
-import com.google.common.collect.Sets;
import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
+ import org.apache.cassandra.db.columniterator.OnDiskAtomIterator;
import org.apache.cassandra.io.sstable.*;
+import org.apache.cassandra.io.sstable.format.SSTableReader;
+import org.apache.cassandra.io.sstable.format.SSTableWriter;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.io.util.RandomAccessReader;
import org.apache.cassandra.service.ActiveRepairService;
@@@ -220,22 -222,11 +222,8 @@@ public class Scrubber implements Closea
if (indexFile != null && dataStart != dataStartFromIndex)
outputHandler.warn(String.format("Data file row position %d differs from index file row position %d", dataStart, dataStartFromIndex));
- SSTableIdentityIterator atoms = new SSTableIdentityIterator(sstable, dataFile, key, checkData);
-
- if (prevKey != null && prevKey.compareTo(key) > 0)
- {
- saveOutOfOrderRow(prevKey, key, atoms);
- continue;
- }
-
- @SuppressWarnings("resource")
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
- if (indexFile != null && dataSize != dataSizeFromIndex)
- outputHandler.warn(String.format("Data file row size %d different from index file row size %d", dataSize, dataSizeFromIndex));
--
- prevKey = key;
- if (tryAppend(prevKey, key, dataSize, writer))
++ if (tryAppend(prevKey, key, writer))
+ prevKey = key;
}
catch (Throwable th)
{
@@@ -251,22 -242,8 +239,9 @@@
try
{
dataFile.seek(dataStartFromIndex);
- if (tryAppend(prevKey, key, dataSize, writer))
+
- SSTableIdentityIterator atoms = new SSTableIdentityIterator(sstable, dataFile, key, checkData);
- if (prevKey != null && prevKey.compareTo(key) > 0)
- {
- saveOutOfOrderRow(prevKey, key, atoms);
- continue;
- }
-
- @SuppressWarnings("resource")
- AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
- if (writer.tryAppend(compactedRow) == null)
- emptyRows++;
- else
- goodRows++;
-
- prevKey = key;
++ if (tryAppend(prevKey, key, writer))
+ prevKey = key;
}
catch (Throwable th2)
{
@@@ -337,6 -316,32 +312,32 @@@
}
}
+ @SuppressWarnings("resource")
- private boolean tryAppend(DecoratedKey prevKey, DecoratedKey key, long dataSize, SSTableRewriter writer)
++ private boolean tryAppend(DecoratedKey prevKey, DecoratedKey key, SSTableRewriter writer)
+ {
+ // OrderCheckerIterator will check, at iteration time, that the cells are in the proper order. If it detects
+ // that one cell is out of order, it will stop returning them. The remaining cells will be sorted and added
+ // to the outOfOrderRows that will be later written to a new SSTable.
- OrderCheckerIterator atoms = new OrderCheckerIterator(new SSTableIdentityIterator(sstable, dataFile, key, dataSize, validateColumns),
++ OrderCheckerIterator atoms = new OrderCheckerIterator(new SSTableIdentityIterator(sstable, dataFile, key, checkData),
+ cfs.metadata.comparator.onDiskAtomComparator());
+ if (prevKey != null && prevKey.compareTo(key) > 0)
+ {
+ saveOutOfOrderRow(prevKey, key, atoms);
+ return false;
+ }
+
+ AbstractCompactedRow compactedRow = new LazilyCompactedRow(controller, Collections.singletonList(atoms));
+ if (writer.tryAppend(compactedRow) == null)
+ emptyRows++;
+ else
+ goodRows++;
+
+ if (atoms.hasOutOfOrderCells())
+ saveOutOfOrderRow(key, atoms);
+
+ return true;
+ }
+
private void updateIndexKey()
{
currentIndexKey = nextIndexKey;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/9583b6b3/src/java/org/apache/cassandra/db/marshal/ReversedType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/ReversedType.java
index 68a7e02,53798f8..19bee5f
--- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
@@@ -60,23 -58,8 +60,13 @@@ public class ReversedType<T> extends Ab
this.baseType = baseType;
}
+ public boolean isEmptyValueMeaningless()
+ {
+ return baseType.isEmptyValueMeaningless();
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
- // An empty byte buffer is always smaller
- if (o1.remaining() == 0)
- {
- return o2.remaining() == 0 ? 0 : -1;
- }
- if (o2.remaining() == 0)
- {
- return 1;
- }
-
return baseType.compare(o2, o1);
}