cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ble...@apache.org
Subject [01/10] cassandra git commit: Fix queries with empty ByteBuffer values in clustering column restrictions
Date Tue, 16 Aug 2016 13:38:41 GMT
Repository: cassandra
Updated Branches:
  refs/heads/cassandra-3.0 6528fbf25 -> bb56193a3


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/cassandra-3.0
Commit: 527d1897c0973491bb0db098ce5ea1e0bd78898f
Parents: bd66547
Author: Benjamin Lerer <b.lerer@gmail.com>
Authored: Tue Aug 16 15:17:59 2016 +0200
Committer: Benjamin Lerer <b.lerer@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;
     }
 }


Mime
View raw message