You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ms...@apache.org on 2016/08/16 22:42:02 UTC

[01/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Repository: cassandra
Updated Branches:
  refs/heads/cassandra-3.8 b188e0826 -> c1e9ad8cd


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/02ebf875
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/02ebf875
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/02ebf875

Branch: refs/heads/cassandra-3.8
Commit: 02ebf875b1a07c79e18bc1f21878c51ed67cb9c4
Parents: ae816cb 676b6a8
Author: Aleksey Yeschenko <al...@apache.org>
Authored: Mon Aug 8 18:41:23 2016 +0100
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Mon Aug 8 18:41:23 2016 +0100

----------------------------------------------------------------------

----------------------------------------------------------------------



[12/50] [abbrv] cassandra git commit: Make sure compaction stats are updated when compaction is interrupted

Posted by ms...@apache.org.
Make sure compaction stats are updated when compaction is interrupted

patch by Stefania Alborghetti; reviewed by Marcus Eriksson for CASSANDRA-12100


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/05483a96
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/05483a96
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/05483a96

Branch: refs/heads/cassandra-3.8
Commit: 05483a962c64c350315fc738c697980b22361cc3
Parents: 2691c9e
Author: Stefania Alborghetti <st...@datastax.com>
Authored: Thu Aug 4 14:20:38 2016 +0800
Committer: Stefania Alborghetti <st...@datastax.com>
Committed: Thu Aug 11 09:09:57 2016 +0800

----------------------------------------------------------------------
 CHANGES.txt                                                     | 1 +
 src/java/org/apache/cassandra/db/compaction/CompactionTask.java | 5 +++--
 2 files changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/05483a96/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index e1bc9f3..bd3bb75 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.9
+ * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
  * Fix potential bad messaging service message for paged range reads
    within mixed-version 3.x clusters (CASSANDRA-12249)
  * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/05483a96/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionTask.java b/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
index 9a7aa98..7e4ed41 100644
--- a/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
+++ b/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
@@ -164,13 +164,14 @@ public class CompactionTask extends AbstractCompactionTask
                  AbstractCompactionStrategy.ScannerList scanners = strategy.getScanners(actuallyCompact);
                  CompactionIterator ci = new CompactionIterator(compactionType, scanners.scanners, controller, nowInSec, taskId))
             {
-                if (collector != null)
-                    collector.beginCompaction(ci);
                 long lastCheckObsoletion = start;
 
                 if (!controller.cfs.getCompactionStrategyManager().isActive)
                     throw new CompactionInterruptedException(ci.getCompactionInfo());
 
+                if (collector != null)
+                    collector.beginCompaction(ci);
+
                 try (CompactionAwareWriter writer = getCompactionAwareWriter(cfs, getDirectories(), transaction, actuallyCompact))
                 {
                     estimatedKeys = writer.estimatedKeys();


[48/50] [abbrv] cassandra git commit: Merge branch cassandra-2.2 into cassandra-3.0

Posted by ms...@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/cassandra-3.8
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


[13/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/15fd71f9
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/15fd71f9
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/15fd71f9

Branch: refs/heads/cassandra-3.8
Commit: 15fd71f9a3b07bbac7a1182f2e6bffd32e79b955
Parents: 1fff069 05483a9
Author: Stefania Alborghetti <st...@datastax.com>
Authored: Thu Aug 11 09:11:11 2016 +0800
Committer: Stefania Alborghetti <st...@datastax.com>
Committed: Thu Aug 11 09:11:11 2016 +0800

----------------------------------------------------------------------
 CHANGES.txt                                                     | 1 +
 src/java/org/apache/cassandra/db/compaction/CompactionTask.java | 5 +++--
 2 files changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/15fd71f9/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 498868c,bd3bb75..bca8879
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,11 -1,7 +1,12 @@@
 -3.0.9
 +3.9
 + * Fix value skipping with counter columns (CASSANDRA-11726)
 + * Fix nodetool tablestats miss SSTable count (CASSANDRA-12205)
 + * Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
 + * Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)
 + * cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
 + * Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
 +Merged from 3.0:
+  * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
 - * Fix potential bad messaging service message for paged range reads
 -   within mixed-version 3.x clusters (CASSANDRA-12249)
   * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
   * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
   * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/15fd71f9/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/compaction/CompactionTask.java
index 86c8a8f,7e4ed41..2913dc9
--- a/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
+++ b/src/java/org/apache/cassandra/db/compaction/CompactionTask.java
@@@ -170,13 -164,14 +170,14 @@@ public class CompactionTask extends Abs
                   AbstractCompactionStrategy.ScannerList scanners = strategy.getScanners(actuallyCompact);
                   CompactionIterator ci = new CompactionIterator(compactionType, scanners.scanners, controller, nowInSec, taskId))
              {
-                 if (collector != null)
-                     collector.beginCompaction(ci);
                  long lastCheckObsoletion = start;
  
 -                if (!controller.cfs.getCompactionStrategyManager().isActive)
 +                if (!controller.cfs.getCompactionStrategyManager().isActive())
                      throw new CompactionInterruptedException(ci.getCompactionInfo());
  
+                 if (collector != null)
+                     collector.beginCompaction(ci);
+ 
                  try (CompactionAwareWriter writer = getCompactionAwareWriter(cfs, getDirectories(), transaction, actuallyCompact))
                  {
                      estimatedKeys = writer.estimatedKeys();


[18/50] [abbrv] cassandra git commit: Add Sigar to classes included in clientutil.java

Posted by ms...@apache.org.
Add Sigar to classes included in clientutil.java

This is required as Sigar is now a dependency of UUIDGen.

Patch by Sylvain Lebresne; reviewed by Tyler Hobbs for CASSANDRA-11635


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5b38d3a8
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5b38d3a8
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5b38d3a8

Branch: refs/heads/cassandra-3.8
Commit: 5b38d3a81e171eb414ae144191a4ff6423bef8b0
Parents: 2e90259
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Thu Aug 11 13:41:22 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Thu Aug 11 13:41:22 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt | 1 +
 build.xml   | 4 ++++
 2 files changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b38d3a8/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 05059cc..14bed81 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.8
+ * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b38d3a8/build.xml
----------------------------------------------------------------------
diff --git a/build.xml b/build.xml
index 49a0366..2426c91 100644
--- a/build.xml
+++ b/build.xml
@@ -917,6 +917,7 @@
           <include name="org/apache/cassandra/exceptions/*.class" />
           <include name="org/apache/cassandra/utils/CloseableIterator.class" />
           <include name="org/apache/cassandra/io/util/*.class" />
+          <include name="org/apache/cassandra/utils/SigarLibrary.class" />
         </fileset>
         <manifest>
           <attribute name="Implementation-Title" value="Cassandra"/>
@@ -1255,6 +1256,8 @@
   -->
   <target name="test-clientutil-jar" depends="build-test,jar" description="Test clientutil jar">
     <junit fork="on" forkmode="perTest" failureproperty="testfailed" maxmemory="1024m" timeout="${test.timeout}">
+      <!-- Note that the test pass without that next line, but it prints an ugly error message -->
+      <jvmarg value="-Djava.library.path=${build.lib}/sigar-bin"/>
       <test name="org.apache.cassandra.serializers.ClientUtilsTest" />
       <formatter type="brief" usefile="false" />
       <classpath>
@@ -1268,6 +1271,7 @@
         <pathelement location="${build.lib}/logback-classic-1.1.3.jar" />
         <pathelement location="${build.lib}/jackson-core-asl-1.9.2.jar" />
         <pathelement location="${build.lib}/jackson-mapper-asl-1.9.2.jar" />
+        <pathelement location="${build.lib}/sigar-1.6.4.jar" />
         <fileset dir="${build.dir.lib}">
           <include name="**/junit*.jar" />
         </fileset>


[27/50] [abbrv] cassandra git commit: avoid some date math if using raw time formats

Posted by ms...@apache.org.
avoid some date math if using raw time formats


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/d052c904
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d052c904
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d052c904

Branch: refs/heads/cassandra-3.8
Commit: d052c90483e44ab8b280c4507d5c11202367dffd
Parents: ca556c4
Author: Dave Brosius <db...@mebigfatguy.com>
Authored: Fri Aug 12 19:13:13 2016 -0400
Committer: Dave Brosius <db...@mebigfatguy.com>
Committed: Fri Aug 12 19:13:13 2016 -0400

----------------------------------------------------------------------
 src/java/org/apache/cassandra/tools/JsonTransformer.java | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/d052c904/src/java/org/apache/cassandra/tools/JsonTransformer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/JsonTransformer.java b/src/java/org/apache/cassandra/tools/JsonTransformer.java
index 3b98595..801197d 100644
--- a/src/java/org/apache/cassandra/tools/JsonTransformer.java
+++ b/src/java/org/apache/cassandra/tools/JsonTransformer.java
@@ -461,9 +461,14 @@ public final class JsonTransformer
 
     private String dateString(TimeUnit from, long time)
     {
+        if (rawTime)
+        {
+            return Long.toString(time);
+        }
+        
         long secs = from.toSeconds(time);
         long offset = Math.floorMod(from.toNanos(time), 1000_000_000L); // nanos per sec
-        return rawTime? Long.toString(time) : Instant.ofEpochSecond(secs, offset).toString();
+        return Instant.ofEpochSecond(secs, offset).toString();
     }
 
     /**


[10/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2' into cassandra-3.0

Posted by ms...@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/2691c9e4
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/2691c9e4
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/2691c9e4

Branch: refs/heads/cassandra-3.8
Commit: 2691c9e4e12a72f143bb2f00d076ecbfad59f4e4
Parents: 465def8 2e90259
Author: T Jake Luciani <ja...@apache.org>
Authored: Wed Aug 10 14:39:52 2016 -0400
Committer: T Jake Luciani <ja...@apache.org>
Committed: Wed Aug 10 14:39:52 2016 -0400

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../metrics/CassandraMetricsRegistry.java       |   4 +-
 .../cassandra/metrics/ClearableHistogram.java   |   4 +-
 .../DecayingEstimatedHistogramReservoir.java    | 549 +++++++++++++++++++
 .../metrics/EstimatedHistogramReservoir.java    | 111 ----
 .../cassandra/utils/EstimatedHistogram.java     |   2 +-
 ...DecayingEstimatedHistogramReservoirTest.java | 381 +++++++++++++
 7 files changed, 936 insertions(+), 116 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/2691c9e4/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 86746ad,05059cc..e1bc9f3
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,37 -1,7 +1,38 @@@
 -2.2.8
 +3.0.9
 + * Fix potential bad messaging service message for paged range reads
 +   within mixed-version 3.x clusters (CASSANDRA-12249)
 + * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
 + * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
 + * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
 + * Fix upgrade of super columns on thrift (CASSANDRA-12335)
 + * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
 + * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
 + * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
 + * Lost counter writes in compact table and static columns (CASSANDRA-12219)
 + * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
 + * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
 + * Add option to override compaction space check (CASSANDRA-12180)
 + * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
 + * Respond with v1/v2 protocol header when responding to driver that attempts
 +   to connect with too low of a protocol version (CASSANDRA-11464)
 + * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
 + * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
 + * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
 + * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
 + * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
 + * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
 + * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
 + * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
 + * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
 + * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
 + * Fix column ordering of results with static columns for Thrift requests in
 +   a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
 +   those static columns in query results (CASSANDRA-12123)
 + * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
 + * Fix EOF exception when altering column type (CASSANDRA-11820)
 +Merged from 2.2:
+  * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2691c9e4/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
index 1cd3b6c,8e5671b..2d4cbf9
--- a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
+++ b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
@@@ -65,31 -58,17 +65,31 @@@ public class CassandraMetricsRegistry e
          return meter;
      }
  
 +    public Meter meter(MetricName name, MetricName alias)
 +    {
 +        Meter meter = meter(name);
 +        registerAlias(name, alias);
 +        return meter;
 +    }
 +
      public Histogram histogram(MetricName name, boolean considerZeroes)
      {
-         Histogram histogram = register(name, new ClearableHistogram(new EstimatedHistogramReservoir(considerZeroes)));
+         Histogram histogram = register(name, new ClearableHistogram(new DecayingEstimatedHistogramReservoir(considerZeroes)));
          registerMBean(histogram, name.getMBeanName());
  
          return histogram;
      }
  
 +    public Histogram histogram(MetricName name, MetricName alias, boolean considerZeroes)
 +    {
 +        Histogram histogram = histogram(name, considerZeroes);
 +        registerAlias(name, alias);
 +        return histogram;
 +    }
 +
      public Timer timer(MetricName name)
      {
-         Timer timer = register(name, new Timer(new EstimatedHistogramReservoir(false)));
+         Timer timer = register(name, new Timer(new DecayingEstimatedHistogramReservoir()));
          registerMBean(timer, name.getMBeanName());
  
          return timer;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2691c9e4/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
----------------------------------------------------------------------


[24/50] [abbrv] cassandra git commit: Fix clean interval not sent to commit log for empty memtable flush

Posted by ms...@apache.org.
Fix clean interval not sent to commit log for empty memtable flush

patch by Branimir Lambov; reviewed by Jake Luciani for
CASSANDRA-12436


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5cef78af
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5cef78af
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5cef78af

Branch: refs/heads/cassandra-3.8
Commit: 5cef78af9214308ef817de43ec36054a2549329c
Parents: c85749e
Author: Branimir Lambov <br...@datastax.com>
Authored: Thu Aug 11 11:15:44 2016 +0300
Committer: T Jake Luciani <ja...@apache.org>
Committed: Fri Aug 12 11:24:16 2016 -0400

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../apache/cassandra/db/ColumnFamilyStore.java  | 19 +++------
 .../cassandra/db/commitlog/CommitLog.java       |  5 ++-
 .../db/commitlog/CommitLogSegment.java          |  5 ++-
 .../db/commitlog/CommitLogCQLTest.java          | 41 ++++++++++++++++++++
 5 files changed, 54 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 959b967..cccb62d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.9
+ * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
  * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
  * Backport CASSANDRA-12002 (CASSANDRA-12177)
  * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index 82604e2..6c02909 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -1039,20 +1039,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
             writeBarrier.markBlocking();
             writeBarrier.await();
 
-            // mark all memtables as flushing, removing them from the live memtable list, and
-            // remove any memtables that are already clean from the set we need to flush
-            Iterator<Memtable> iter = memtables.iterator();
-            while (iter.hasNext())
-            {
-                Memtable memtable = iter.next();
+            // mark all memtables as flushing, removing them from the live memtable list
+            for (Memtable memtable : memtables)
                 memtable.cfs.data.markFlushing(memtable);
-                if (memtable.isClean() || truncate)
-                {
-                    memtable.cfs.replaceFlushed(memtable, Collections.emptyList());
-                    reclaim(memtable);
-                    iter.remove();
-                }
-            }
 
             metric.memtableSwitchCount.inc();
 
@@ -1060,7 +1049,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
             {
                 for (Memtable memtable : memtables)
                 {
-                    Collection<SSTableReader> readers = memtable.flush();
+                    Collection<SSTableReader> readers = Collections.emptyList();
+                    if (!memtable.isClean() && !truncate)
+                        readers = memtable.flush();
                     memtable.cfs.replaceFlushed(memtable, readers);
                     reclaim(memtable);
                 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
index dfe3f91..d167735 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
@@ -313,8 +313,9 @@ public class CommitLog implements CommitLogMBean
             }
             else
             {
-                logger.trace("Not safe to delete{} commit log segment {}; dirty is {}",
-                        (iter.hasNext() ? "" : " active"), segment, segment.dirtyString());
+                if (logger.isTraceEnabled())
+                    logger.trace("Not safe to delete{} commit log segment {}; dirty is {}",
+                            (iter.hasNext() ? "" : " active"), segment, segment.dirtyString());
             }
 
             // Don't mark or try to delete any newer segments once we've reached the one containing the

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
index d2f12bf..0a03c3c 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
@@ -536,7 +536,10 @@ public abstract class CommitLogSegment
         for (UUID cfId : getDirtyCFIDs())
         {
             CFMetaData m = Schema.instance.getCFMetaData(cfId);
-            sb.append(m == null ? "<deleted>" : m.cfName).append(" (").append(cfId).append("), ");
+            sb.append(m == null ? "<deleted>" : m.cfName).append(" (").append(cfId)
+              .append(", dirty: ").append(cfDirty.get(cfId))
+              .append(", clean: ").append(cfClean.get(cfId))
+              .append("), ");
         }
         return sb.toString();
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5cef78af/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java b/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
new file mode 100644
index 0000000..8537ebb
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
@@ -0,0 +1,41 @@
+package org.apache.cassandra.db.commitlog;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.junit.Test;
+
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.db.ColumnFamilyStore;
+
+public class CommitLogCQLTest extends CQLTester
+{
+    @Test
+    public void testTruncateSegmentDiscard() throws Throwable
+    {
+        String otherTable = createTable("CREATE TABLE %s (idx INT, data TEXT, PRIMARY KEY(idx));");
+
+        createTable("CREATE TABLE %s (idx INT, data TEXT, PRIMARY KEY(idx));");
+        execute("INSERT INTO %s (idx, data) VALUES (?, ?)", 15, Integer.toString(15));
+        flush();
+
+        // We write something in different table to advance the commit log position. Current table remains clean.
+        execute(String.format("INSERT INTO %s.%s (idx, data) VALUES (?, ?)", keyspace(), otherTable), 16, Integer.toString(16));
+
+        ColumnFamilyStore cfs = getCurrentColumnFamilyStore();
+        assert cfs.getTracker().getView().getCurrentMemtable().isClean();
+        // Calling switchMemtable directly applies Flush even though memtable is empty. This can happen with some races
+        // (flush with recycling by segment manager). It should still tell commitlog that the memtable's region is clean.
+        // CASSANDRA-12436
+        cfs.switchMemtable();
+
+        execute("INSERT INTO %s (idx, data) VALUES (?, ?)", 15, Integer.toString(17));
+
+        Collection<CommitLogSegment> active = new ArrayList<>(CommitLog.instance.allocator.getActiveSegments());
+        CommitLog.instance.forceRecycleAllSegments();
+
+        // If one of the previous segments remains, it wasn't clean.
+        active.retainAll(CommitLog.instance.allocator.getActiveSegments());
+        assert active.isEmpty();
+    }
+}


[19/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2' into cassandra-3.0

Posted by ms...@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/2a828afd
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/2a828afd
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/2a828afd

Branch: refs/heads/cassandra-3.8
Commit: 2a828afdbfc7410b288c1681506dcbeecb9df3c2
Parents: 19245e5 5b38d3a
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Thu Aug 11 13:46:47 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Thu Aug 11 13:46:47 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt | 1 +
 build.xml   | 4 ++++
 2 files changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/2a828afd/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 0a1d406,14bed81..3ce9f9e
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,41 -1,8 +1,42 @@@
 -2.2.8
 +3.0.9
 + * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
 + * Backport CASSANDRA-12002 (CASSANDRA-12177)
 + * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
 + * Fix potential bad messaging service message for paged range reads
 +   within mixed-version 3.x clusters (CASSANDRA-12249)
 + * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
 + * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
 + * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
 + * Fix upgrade of super columns on thrift (CASSANDRA-12335)
 + * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
 + * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
 + * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
 + * Lost counter writes in compact table and static columns (CASSANDRA-12219)
 + * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
 + * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
 + * Add option to override compaction space check (CASSANDRA-12180)
 + * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
 + * Respond with v1/v2 protocol header when responding to driver that attempts
 +   to connect with too low of a protocol version (CASSANDRA-11464)
 + * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
 + * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
 + * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
 + * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
 + * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
 + * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
 + * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
 + * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
 + * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
 + * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
 + * Fix column ordering of results with static columns for Thrift requests in
 +   a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
 +   those static columns in query results (CASSANDRA-12123)
 + * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
 + * Fix EOF exception when altering column type (CASSANDRA-11820)
 +Merged from 2.2:
+  * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2a828afd/build.xml
----------------------------------------------------------------------


[02/50] [abbrv] cassandra git commit: Fix value skipping with counter columns

Posted by ms...@apache.org.
Fix value skipping with counter columns

patch by Sylvain Lebresne; reviewed by Aleksey Yeschenko for
CASSANDRA-11726


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/ee609411
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/ee609411
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/ee609411

Branch: refs/heads/cassandra-3.8
Commit: ee609411a8e154255812b157788a79bbdf076566
Parents: 02ebf87
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Thu Aug 4 17:54:03 2016 +0200
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Tue Aug 9 16:33:10 2016 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 src/java/org/apache/cassandra/db/Conflicts.java |  9 +++++++
 .../cql3/validation/entities/CountersTest.java  | 27 ++++++++++++++++++++
 3 files changed, 37 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee609411/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 2c5c221..b7bbf72 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.9
+ * Fix value skipping with counter columns (CASSANDRA-11726)
  * Fix nodetool tablestats miss SSTable count (CASSANDRA-12205)
  * Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
  * Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee609411/src/java/org/apache/cassandra/db/Conflicts.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/Conflicts.java b/src/java/org/apache/cassandra/db/Conflicts.java
index fa0e819..9e8bd9a 100644
--- a/src/java/org/apache/cassandra/db/Conflicts.java
+++ b/src/java/org/apache/cassandra/db/Conflicts.java
@@ -68,6 +68,15 @@ public abstract class Conflicts
         if (!rightLive)
             return Resolution.RIGHT_WINS;
 
+        // Handle empty values. Counters can't truly have empty values, but we can have a counter cell that temporarily
+        // has one on read if the column for the cell is not queried by the user due to the optimization of #10657. We
+        // thus need to handle this (see #11726 too).
+        if (!leftValue.hasRemaining())
+            return rightValue.hasRemaining() || leftTimestamp > rightTimestamp ? Resolution.LEFT_WINS : Resolution.RIGHT_WINS;
+
+        if (!rightValue.hasRemaining())
+            return Resolution.RIGHT_WINS;
+
         return Resolution.MERGE;
     }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee609411/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java
index 33b4a4f..b1cd0bb 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CountersTest.java
@@ -196,4 +196,31 @@ public class CountersTest extends CQLTester
         assertInvalidThrowMessage("counter type is not supported for PRIMARY KEY part a",
                                   InvalidRequestException.class, String.format("CREATE TABLE %s.%s (a counter, b int, PRIMARY KEY (b, a)) WITH CLUSTERING ORDER BY (a desc);", KEYSPACE, createTableName()));
     }
+
+    /**
+     * Test for the bug of #11726.
+     */
+    @Test
+    public void testCounterAndColumnSelection() throws Throwable
+    {
+        for (String compactStorageClause : new String[]{ "", " WITH COMPACT STORAGE" })
+        {
+            createTable("CREATE TABLE %s (k int PRIMARY KEY, c counter)" + compactStorageClause);
+
+            // Flush 2 updates in different sstable so that the following select does a merge, which is what triggers
+            // the problem from #11726
+
+            execute("UPDATE %s SET c = c + ? WHERE k = ?", 1L, 0);
+
+            flush();
+
+            execute("UPDATE %s SET c = c + ? WHERE k = ?", 1L, 0);
+
+            flush();
+
+            // Querying, but not including the counter. Pre-CASSANDRA-11726, this made us query the counter but include
+            // it's value, which broke at merge (post-CASSANDRA-11726 are special cases to never skip values).
+            assertRows(execute("SELECT k FROM %s"), row(0));
+        }
+    }
 }


[14/50] [abbrv] cassandra git commit: Backport CASSANDRA-12002

Posted by ms...@apache.org.
Backport CASSANDRA-12002


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/de86ab1b
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/de86ab1b
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/de86ab1b

Branch: refs/heads/cassandra-3.8
Commit: de86ab1bb6e4b71451348397e0cbb2a577cd92d4
Parents: 05483a9
Author: Yuki Morishita <yu...@apache.org>
Authored: Thu Aug 11 10:06:05 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Thu Aug 11 10:11:13 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../apache/cassandra/tools/SSTableExport.java   |  8 ++----
 .../org/apache/cassandra/utils/FBUtilities.java | 28 ++++++++++++++++++++
 3 files changed, 31 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/de86ab1b/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index bd3bb75..3db2c77 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.9
+ * Backport CASSANDRA-12002 (CASSANDRA-12177)
  * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
  * Fix potential bad messaging service message for paged range reads
    within mixed-version 3.x clusters (CASSANDRA-12249)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/de86ab1b/src/java/org/apache/cassandra/tools/SSTableExport.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/SSTableExport.java b/src/java/org/apache/cassandra/tools/SSTableExport.java
index 09dbbed..cc6b84b 100644
--- a/src/java/org/apache/cassandra/tools/SSTableExport.java
+++ b/src/java/org/apache/cassandra/tools/SSTableExport.java
@@ -98,14 +98,10 @@ public class SSTableExport
         if (!desc.version.storeRows())
             throw new IOException("pre-3.0 SSTable is not supported.");
 
-        EnumSet<MetadataType> types = EnumSet.of(MetadataType.VALIDATION, MetadataType.STATS, MetadataType.HEADER);
+        EnumSet<MetadataType> types = EnumSet.of(MetadataType.STATS, MetadataType.HEADER);
         Map<MetadataType, MetadataComponent> sstableMetadata = desc.getMetadataSerializer().deserialize(desc, types);
-        ValidationMetadata validationMetadata = (ValidationMetadata) sstableMetadata.get(MetadataType.VALIDATION);
         SerializationHeader.Component header = (SerializationHeader.Component) sstableMetadata.get(MetadataType.HEADER);
-
-        IPartitioner partitioner = SecondaryIndexManager.isIndexColumnFamily(desc.cfname)
-                                   ? new LocalPartitioner(header.getKeyType())
-                                   : FBUtilities.newPartitioner(validationMetadata.partitioner);
+        IPartitioner partitioner = FBUtilities.newPartitioner(desc);
 
         CFMetaData.Builder builder = CFMetaData.Builder.create("keyspace", "table").withPartitioner(partitioner);
         header.getStaticColumns().entrySet().stream()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/de86ab1b/src/java/org/apache/cassandra/utils/FBUtilities.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/FBUtilities.java b/src/java/org/apache/cassandra/utils/FBUtilities.java
index 5f0e0a0..d996c91 100644
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@ -42,11 +42,17 @@ import org.apache.cassandra.auth.IAuthorizer;
 import org.apache.cassandra.auth.IRoleManager;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.SerializationHeader;
 import org.apache.cassandra.dht.IPartitioner;
+import org.apache.cassandra.dht.LocalPartitioner;
 import org.apache.cassandra.dht.Range;
 import org.apache.cassandra.dht.Token;
 import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.sstable.Descriptor;
+import org.apache.cassandra.io.sstable.metadata.MetadataComponent;
+import org.apache.cassandra.io.sstable.metadata.MetadataType;
+import org.apache.cassandra.io.sstable.metadata.ValidationMetadata;
 import org.apache.cassandra.schema.CompressionParams;
 import org.apache.cassandra.io.util.DataOutputBuffer;
 import org.apache.cassandra.io.util.DataOutputBufferFixed;
@@ -395,6 +401,28 @@ public class FBUtilities
             result.get(ms, TimeUnit.MILLISECONDS);
     }
 
+    /**
+     * Create a new instance of a partitioner defined in an SSTable Descriptor
+     * @param desc Descriptor of an sstable
+     * @return a new IPartitioner instance
+     * @throws IOException
+     */
+    public static IPartitioner newPartitioner(Descriptor desc) throws IOException
+    {
+        EnumSet<MetadataType> types = EnumSet.of(MetadataType.VALIDATION, MetadataType.HEADER);
+        Map<MetadataType, MetadataComponent> sstableMetadata = desc.getMetadataSerializer().deserialize(desc, types);
+        ValidationMetadata validationMetadata = (ValidationMetadata) sstableMetadata.get(MetadataType.VALIDATION);
+        SerializationHeader.Component header = (SerializationHeader.Component) sstableMetadata.get(MetadataType.HEADER);
+        if (validationMetadata.partitioner.endsWith("LocalPartitioner"))
+        {
+            return new LocalPartitioner(header.getKeyType());
+        }
+        else
+        {
+            return newPartitioner(validationMetadata.partitioner);
+        }
+    }
+
     public static IPartitioner newPartitioner(String partitionerClassName) throws ConfigurationException
     {
         if (!partitionerClassName.contains("."))


[25/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/e60f741e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e60f741e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e60f741e

Branch: refs/heads/cassandra-3.8
Commit: e60f741e34eb2ebf80391be640e4a57022554c7f
Parents: 0c771a2 5cef78a
Author: T Jake Luciani <ja...@apache.org>
Authored: Fri Aug 12 11:28:47 2016 -0400
Committer: T Jake Luciani <ja...@apache.org>
Committed: Fri Aug 12 11:28:47 2016 -0400

----------------------------------------------------------------------

----------------------------------------------------------------------



[04/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2' into cassandra-3.0

Posted by ms...@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/62ef8617
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/62ef8617
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/62ef8617

Branch: refs/heads/cassandra-3.8
Commit: 62ef8617cdaa07fa37b1b2121ad5923da64e74a3
Parents: 676b6a8 76e3100
Author: Yuki Morishita <yu...@apache.org>
Authored: Tue Aug 9 16:45:52 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Tue Aug 9 16:45:52 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../org/apache/cassandra/config/Config.java     |  4 ++
 .../cassandra/config/DatabaseDescriptor.java    |  5 --
 .../cassandra/streaming/StreamReader.java       | 26 +---------
 .../cassandra/streaming/StreamSession.java      | 36 +-------------
 .../compress/CompressedInputStream.java         | 21 +++++++-
 .../compress/CompressedStreamReader.java        | 11 ++---
 .../streaming/messages/IncomingFileMessage.java | 22 ++-------
 .../streaming/messages/RetryMessage.java        |  4 ++
 .../org/apache/cassandra/utils/Throwables.java  | 14 ++++++
 .../compression/CompressedInputStreamTest.java  | 52 +++++++++++++++++---
 11 files changed, 100 insertions(+), 96 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 78bd32d,232203e..f613c5f
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,34 -1,6 +1,35 @@@
 -2.2.8
 +3.0.9
 + * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
 + * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
 + * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
 + * Fix upgrade of super columns on thrift (CASSANDRA-12335)
 + * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
 + * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
 + * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
 + * Lost counter writes in compact table and static columns (CASSANDRA-12219)
 + * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
 + * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
 + * Add option to override compaction space check (CASSANDRA-12180)
 + * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
 + * Respond with v1/v2 protocol header when responding to driver that attempts
 +   to connect with too low of a protocol version (CASSANDRA-11464)
 + * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
 + * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
 + * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
 + * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
 + * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
 + * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
 + * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
 + * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
 + * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
 + * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
 + * Fix column ordering of results with static columns for Thrift requests in
 +   a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
 +   those static columns in query results (CASSANDRA-12123)
 + * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
 + * Fix EOF exception when altering column type (CASSANDRA-11820)
 +Merged from 2.2:
+  * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/config/Config.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/config/Config.java
index e6c56cb,60daee6..86f1016
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@@ -171,8 -170,11 +171,12 @@@ public class Confi
      public Integer concurrent_compactors;
      public volatile Integer compaction_throughput_mb_per_sec = 16;
      public volatile Integer compaction_large_partition_warning_threshold_mb = 100;
 +    public Integer min_free_space_per_drive_in_mb = 50;
  
+     /**
+      * @deprecated retry support removed on CASSANDRA-10992
+      */
+     @Deprecated
      public Integer max_streaming_retries = 3;
  
      public volatile Integer stream_throughput_outbound_megabits_per_sec = 200;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/StreamReader.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/StreamReader.java
index f8db26b,c96ea22..4ca7937
--- a/src/java/org/apache/cassandra/streaming/StreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/StreamReader.java
@@@ -31,24 -33,19 +31,25 @@@ import org.slf4j.LoggerFactory
  
  import com.ning.compress.lzf.LZFInputStream;
  
 +import org.apache.cassandra.config.CFMetaData;
  import org.apache.cassandra.config.Schema;
 -import org.apache.cassandra.db.ColumnFamilyStore;
 -import org.apache.cassandra.db.DecoratedKey;
 -import org.apache.cassandra.db.Directories;
 -import org.apache.cassandra.db.Keyspace;
 +import org.apache.cassandra.db.*;
 +import org.apache.cassandra.db.rows.*;
  import org.apache.cassandra.io.sstable.Descriptor;
 -import org.apache.cassandra.service.StorageService;
 +import org.apache.cassandra.io.sstable.SSTableMultiWriter;
 +import org.apache.cassandra.io.sstable.SSTableSimpleIterator;
 +import org.apache.cassandra.io.sstable.format.SSTableFormat;
 +import org.apache.cassandra.io.sstable.format.Version;
 +import org.apache.cassandra.io.util.RewindableDataInputStreamPlus;
 +import org.apache.cassandra.io.util.DataInputPlus;
 +import org.apache.cassandra.net.MessagingService;
  import org.apache.cassandra.streaming.messages.FileMessageHeader;
  import org.apache.cassandra.utils.ByteBufferUtil;
 -import org.apache.cassandra.utils.BytesReadTracker;
 +import org.apache.cassandra.io.util.TrackedInputStream;
 +import org.apache.cassandra.utils.FBUtilities;
  import org.apache.cassandra.utils.Pair;
  
+ import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
  
  /**
   * StreamReader reads from stream and writes to SSTable.
@@@ -124,63 -121,37 +125,40 @@@ public class StreamReade
              logger.debug("[Stream #{}] Finished receiving file #{} from {} readBytes = {}, totalSize = {}",
                           session.planId(), fileSeqNum, session.peer, in.getBytesRead(), totalSize);
              return writer;
 -        } catch (Throwable e)
 +        }
 +        catch (Throwable e)
          {
 -            if (key != null)
 +            if (deserializer != null)
                  logger.warn("[Stream {}] Error while reading partition {} from stream on ks='{}' and table='{}'.",
 -                            session.planId(), key, cfs.keyspace.getName(), cfs.getColumnFamilyName());
 +                            session.planId(), deserializer.partitionKey(), cfs.keyspace.getName(), cfs.getColumnFamilyName());
              if (writer != null)
              {
 -                try
 -                {
 -                    writer.abort();
 -                }
 -                catch (Throwable e2)
 -                {
 -                    // add abort error to original and continue so we can drain unread stream
 -                    e.addSuppressed(e2);
 -                }
 +                writer.abort(e);
              }
-             drain(in, in.getBytesRead());
-             if (e instanceof IOException)
-                 throw (IOException) e;
-             else
-                 throw Throwables.propagate(e);
+             throw Throwables.propagate(e);
          }
 +        finally
 +        {
 +            if (deserializer != null)
 +                deserializer.cleanup();
 +        }
 +    }
 +
 +    protected SerializationHeader getHeader(CFMetaData metadata)
 +    {
 +        return header != null? header.toHeader(metadata) : null; //pre-3.0 sstable have no SerializationHeader
      }
  
 -    protected SSTableWriter createWriter(ColumnFamilyStore cfs, long totalSize, long repairedAt, SSTableFormat.Type format) throws IOException
 +    protected SSTableMultiWriter createWriter(ColumnFamilyStore cfs, long totalSize, long repairedAt, SSTableFormat.Type format) throws IOException
      {
 -        Directories.DataDirectory localDir = cfs.directories.getWriteableLocation(totalSize);
 +        Directories.DataDirectory localDir = cfs.getDirectories().getWriteableLocation(totalSize);
          if (localDir == null)
              throw new IOException("Insufficient disk space to store " + totalSize + " bytes");
 -        desc = Descriptor.fromFilename(cfs.getTempSSTablePath(cfs.directories.getLocationForDisk(localDir), format));
 +        desc = Descriptor.fromFilename(cfs.getSSTablePath(cfs.getDirectories().getLocationForDisk(localDir), format));
  
 -        return SSTableWriter.create(desc, estimatedKeys, repairedAt, sstableLevel);
 +        return cfs.createSSTableMultiWriter(desc, estimatedKeys, repairedAt, sstableLevel, getHeader(cfs.metadata), session.getTransaction(cfId));
      }
  
-     protected void drain(InputStream dis, long bytesRead) throws IOException
-     {
-         long toSkip = totalSize() - bytesRead;
- 
-         // InputStream.skip can return -1 if dis is inaccessible.
-         long skipped = dis.skip(toSkip);
-         if (skipped == -1)
-             return;
- 
-         toSkip = toSkip - skipped;
-         while (toSkip > 0)
-         {
-             skipped = dis.skip(toSkip);
-             if (skipped == -1)
-                 break;
-             toSkip = toSkip - skipped;
-         }
-     }
- 
      protected long totalSize()
      {
          long size = 0;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/StreamSession.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
index 9719587,fa1022d..bc87c8f
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
@@@ -17,7 -17,8 +17,6 @@@
   */
  package org.apache.cassandra.streaming.compress;
  
--import java.io.DataInputStream;
 -
  import java.io.IOException;
  import java.nio.channels.Channels;
  import java.nio.channels.ReadableByteChannel;
@@@ -37,9 -40,12 +36,11 @@@ import org.apache.cassandra.streaming.P
  import org.apache.cassandra.streaming.StreamReader;
  import org.apache.cassandra.streaming.StreamSession;
  import org.apache.cassandra.streaming.messages.FileMessageHeader;
 -import org.apache.cassandra.utils.ByteBufferUtil;
 -import org.apache.cassandra.utils.BytesReadTracker;
 +import org.apache.cassandra.io.util.TrackedInputStream;
  import org.apache.cassandra.utils.Pair;
  
+ import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
+ 
  /**
   * StreamReader that reads from streamed compressed SSTable
   */
@@@ -114,24 -119,25 +115,22 @@@ public class CompressedStreamReader ext
          }
          catch (Throwable e)
          {
 -            if (key != null)
 +            if (deserializer != null)
                  logger.warn("[Stream {}] Error while reading partition {} from stream on ks='{}' and table='{}'.",
 -                            session.planId(), key, cfs.keyspace.getName(), cfs.getColumnFamilyName());
 +                            session.planId(), deserializer.partitionKey(), cfs.keyspace.getName(), cfs.getTableName());
              if (writer != null)
              {
 -                try
 -                {
 -                    writer.abort();
 -                }
 -                catch (Throwable e2)
 -                {
 -                    // add abort error to original and continue so we can drain unread stream
 -                    e.addSuppressed(e2);
 -                }
 +                writer.abort(e);
              }
-             drain(in, in.getBytesRead());
-             if (e instanceof IOException)
-                 throw (IOException) e;
-             else
-                 throw Throwables.propagate(e);
+             if (extractIOExceptionCause(e).isPresent())
+                 throw e;
+             throw Throwables.propagate(e);
          }
 +        finally
 +        {
 +            if (deserializer != null)
 +                deserializer.cleanup();
 +        }
      }
  
      @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
index d881d43,2870c03..438cb0b
--- a/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
+++ b/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
@@@ -20,10 -20,11 +20,12 @@@ package org.apache.cassandra.streaming.
  import java.io.IOException;
  import java.nio.channels.Channels;
  import java.nio.channels.ReadableByteChannel;
++import java.util.Optional;
  
 -import com.google.common.base.Optional;
 +import org.apache.cassandra.io.sstable.SSTableMultiWriter;
 +import org.apache.cassandra.io.util.DataInputPlus;
 +import org.apache.cassandra.io.util.DataInputPlus.DataInputStreamPlus;
+ 
 -import org.apache.cassandra.io.sstable.format.SSTableWriter;
  import org.apache.cassandra.io.util.DataOutputStreamPlus;
  import org.apache.cassandra.streaming.StreamReader;
  import org.apache.cassandra.streaming.StreamSession;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/src/java/org/apache/cassandra/utils/Throwables.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/utils/Throwables.java
index 8ef6a63,877f388..5ad9686
--- a/src/java/org/apache/cassandra/utils/Throwables.java
+++ b/src/java/org/apache/cassandra/utils/Throwables.java
@@@ -18,25 -18,14 +18,26 @@@
  */
  package org.apache.cassandra.utils;
  
 +import java.io.File;
  import java.io.IOException;
 +import java.util.Arrays;
 +import java.util.Iterator;
++import java.util.Optional;
 +import java.util.stream.Stream;
  
 -import com.google.common.base.Optional;
 +import org.apache.cassandra.io.FSReadError;
 +import org.apache.cassandra.io.FSWriteError;
  
 -public class Throwables
 +public final class Throwables
  {
 +    public enum FileOpType { READ, WRITE }
  
 -    public static Throwable merge(Throwable existingFail, Throwable newFail)
 +    public interface DiscreteAction<E extends Exception>
 +    {
 +        void perform() throws E;
 +    }
 +
 +    public static <T extends Throwable> T merge(T existingFail, T newFail)
      {
          if (existingFail == null)
              return newFail;
@@@ -167,4 -54,17 +168,17 @@@
          }
          return accumulate;
      }
+ 
+     public static Optional<IOException> extractIOExceptionCause(Throwable t)
+     {
+         if (t instanceof IOException)
+             return Optional.of((IOException) t);
+         Throwable cause = t;
+         while ((cause = cause.getCause()) != null)
+         {
+             if (cause instanceof IOException)
+                 return Optional.of((IOException) cause);
+         }
 -        return Optional.absent();
++        return Optional.empty();
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/62ef8617/test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
index a3300ac,0000000..8512d8f
mode 100644,000000..100644
--- a/test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
+++ b/test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
@@@ -1,137 -1,0 +1,173 @@@
 +/*
 + * 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.streaming.compression;
 +
 +import java.io.*;
 +import java.util.*;
 +import java.util.concurrent.SynchronousQueue;
 +import java.util.concurrent.TimeUnit;
 +
 +import org.junit.Test;
 +import org.apache.cassandra.db.ClusteringComparator;
 +import org.apache.cassandra.db.marshal.BytesType;
 +import org.apache.cassandra.io.compress.CompressedSequentialWriter;
 +import org.apache.cassandra.io.compress.CompressionMetadata;
 +import org.apache.cassandra.schema.CompressionParams;
 +import org.apache.cassandra.io.sstable.Component;
 +import org.apache.cassandra.io.sstable.Descriptor;
 +import org.apache.cassandra.io.sstable.metadata.MetadataCollector;
 +import org.apache.cassandra.streaming.compress.CompressedInputStream;
 +import org.apache.cassandra.streaming.compress.CompressionInfo;
 +import org.apache.cassandra.utils.ChecksumType;
 +import org.apache.cassandra.utils.Pair;
 +
 +import static org.junit.Assert.assertEquals;
++import static org.junit.Assert.fail;
 +
 +/**
 + */
 +public class CompressedInputStreamTest
 +{
 +    @Test
 +    public void testCompressedRead() throws Exception
 +    {
-         testCompressedReadWith(new long[]{0L}, false);
-         testCompressedReadWith(new long[]{1L}, false);
-         testCompressedReadWith(new long[]{100L}, false);
++        testCompressedReadWith(new long[]{0L}, false, false);
++        testCompressedReadWith(new long[]{1L}, false, false);
++        testCompressedReadWith(new long[]{100L}, false, false);
 +
-         testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false);
++        testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false, false);
 +    }
 +
 +    @Test(expected = EOFException.class)
 +    public void testTruncatedRead() throws Exception
 +    {
-         testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, true);
++        testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, true, false);
++    }
++
++    /**
++     * Test that CompressedInputStream does not block if there's an exception while reading stream
++     */
++    @Test(timeout = 30000)
++    public void testException() throws Exception
++    {
++        testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false, true);
 +    }
 +
 +    /**
 +     * @param valuesToCheck array of longs of range(0-999)
 +     * @throws Exception
 +     */
-     private void testCompressedReadWith(long[] valuesToCheck, boolean testTruncate) throws Exception
++    private void testCompressedReadWith(long[] valuesToCheck, boolean testTruncate, boolean testException) throws Exception
 +    {
 +        assert valuesToCheck != null && valuesToCheck.length > 0;
 +
 +        // write compressed data file of longs
 +        File tmp = new File(File.createTempFile("cassandra", "unittest").getParent(), "ks-cf-ib-1-Data.db");
 +        Descriptor desc = Descriptor.fromFilename(tmp.getAbsolutePath());
 +        MetadataCollector collector = new MetadataCollector(new ClusteringComparator(BytesType.instance));
 +        CompressionParams param = CompressionParams.snappy(32);
 +        Map<Long, Long> index = new HashMap<Long, Long>();
 +        try (CompressedSequentialWriter writer = new CompressedSequentialWriter(tmp, desc.filenameFor(Component.COMPRESSION_INFO), param, collector))
 +        {
 +            for (long l = 0L; l < 1000; l++)
 +            {
 +                index.put(l, writer.position());
 +                writer.writeLong(l);
 +            }
 +            writer.finish();
 +        }
 +
 +        CompressionMetadata comp = CompressionMetadata.create(tmp.getAbsolutePath());
 +        List<Pair<Long, Long>> sections = new ArrayList<>();
 +        for (long l : valuesToCheck)
 +        {
 +            long position = index.get(l);
 +            sections.add(Pair.create(position, position + 8));
 +        }
 +        CompressionMetadata.Chunk[] chunks = comp.getChunksForSections(sections);
 +        long totalSize = comp.getTotalSizeForSections(sections);
 +        long expectedSize = 0;
 +        for (CompressionMetadata.Chunk c : chunks)
 +            expectedSize += c.length + 4;
 +        assertEquals(expectedSize, totalSize);
 +
 +        // buffer up only relevant parts of file
 +        int size = 0;
 +        for (CompressionMetadata.Chunk c : chunks)
 +            size += (c.length + 4); // 4bytes CRC
 +        byte[] toRead = new byte[size];
 +
 +        try (RandomAccessFile f = new RandomAccessFile(tmp, "r"))
 +        {
 +            int pos = 0;
 +            for (CompressionMetadata.Chunk c : chunks)
 +            {
 +                f.seek(c.offset);
 +                pos += f.read(toRead, pos, c.length + 4);
 +            }
 +        }
 +
 +        if (testTruncate)
 +        {
 +            byte [] actuallyRead = new byte[50];
 +            System.arraycopy(toRead, 0, actuallyRead, 0, 50);
 +            toRead = actuallyRead;
 +        }
 +
 +        // read buffer using CompressedInputStream
 +        CompressionInfo info = new CompressionInfo(chunks, param);
-         CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(toRead), info,
-                                                                 ChecksumType.CRC32, () -> 1.0);
++
++        if (testException)
++        {
++            testException(sections, info);
++            return;
++        }
++        CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(toRead), info, ChecksumType.CRC32, () -> 1.0);
 +
 +        try (DataInputStream in = new DataInputStream(input))
 +        {
 +            for (int i = 0; i < sections.size(); i++)
 +            {
 +                input.position(sections.get(i).left);
 +                long readValue = in.readLong();
 +                assertEquals("expected " + valuesToCheck[i] + " but was " + readValue, valuesToCheck[i], readValue);
 +            }
 +        }
 +    }
++
++    private static void testException(List<Pair<Long, Long>> sections, CompressionInfo info) throws IOException
++    {
++        CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(new byte[0]), info, ChecksumType.CRC32, () -> 1.0);
++
++        try (DataInputStream in = new DataInputStream(input))
++        {
++            for (int i = 0; i < sections.size(); i++)
++            {
++                input.position(sections.get(i).left);
++                try {
++                    in.readLong();
++                    fail("Should have thrown IOException");
++                }
++                catch (IOException e)
++                {
++                    continue;
++                }
++            }
++        }
++    }
 +}


[41/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into cassandra-2.2

Posted by ms...@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)


[50/50] [abbrv] cassandra git commit: Update CHANGES.txt for 3.8

Posted by ms...@apache.org.
Update CHANGES.txt for 3.8


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/c1e9ad8c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c1e9ad8c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c1e9ad8c

Branch: refs/heads/cassandra-3.8
Commit: c1e9ad8cde9f0eec6c87fc19db45460eb8e8f500
Parents: cb97649
Author: Michael Shuler <ms...@apache.org>
Authored: Tue Aug 16 17:37:37 2016 -0500
Committer: Michael Shuler <ms...@apache.org>
Committed: Tue Aug 16 17:37:37 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt | 54 ++++++++++++++++++++----------------------------------
 1 file changed, 20 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/c1e9ad8c/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 2c83c42..b27d974 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,43 +1,10 @@
-3.9
+3.8
  * Fix value skipping with counter columns (CASSANDRA-11726)
  * Fix nodetool tablestats miss SSTable count (CASSANDRA-12205)
  * Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
  * Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)
  * cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
  * Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
-Merged from 3.0:
- * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
- * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
- * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
- * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
- * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
- * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
- * Fix upgrade of super columns on thrift (CASSANDRA-12335)
- * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
- * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
- * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
- * Lost counter writes in compact table and static columns (CASSANDRA-12219)
- * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
- * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
- * Add option to override compaction space check (CASSANDRA-12180)
- * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
- * Respond with v1/v2 protocol header when responding to driver that attempts
-   to connect with too low of a protocol version (CASSANDRA-11464)
- * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
- * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
-Merged from 2.2:
- * Fix hanging stream session (CASSANDRA-10992)
- * Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
- * Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
- * 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)
-Merged from 2.1:
- * cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
- * Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
-
-
-3.8
  * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
  * Fix hdr logging for single operation workloads (CASSANDRA-12145)
  * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
@@ -67,6 +34,25 @@ Merged from 2.1:
  * Add repaired percentage metric (CASSANDRA-11503)
  * Add Change-Data-Capture (CASSANDRA-8844)
 Merged from 3.0:
+ * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
+ * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
+ * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
+ * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
+ * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
+ * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
+ * Fix upgrade of super columns on thrift (CASSANDRA-12335)
+ * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
+ * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
+ * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
+ * Lost counter writes in compact table and static columns (CASSANDRA-12219)
+ * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
+ * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
+ * Add option to override compaction space check (CASSANDRA-12180)
+ * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
+ * Respond with v1/v2 protocol header when responding to driver that attempts
+   to connect with too low of a protocol version (CASSANDRA-11464)
+ * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
+ * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
  * Fix potential bad messaging service message for paged range reads
    within mixed-version 3.x clusters (CASSANDRA-12249)
  * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)


[46/50] [abbrv] cassandra git commit: Merge branch cassandra-2.2 into cassandra-3.0

Posted by ms...@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")));
 -            }
++            });
+         }
+     }
  }


[37/50] [abbrv] cassandra git commit: Add schema to snapshot manifest, add WITH TIMESTAMP to DROP statement

Posted by ms...@apache.org.
Add schema to snapshot manifest, add WITH TIMESTAMP to DROP statement

Patch by Alex Petrov; reviewed by Aleksey Yeschenko for CASSANDRA-7190


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/6528fbf2
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/6528fbf2
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/6528fbf2

Branch: refs/heads/cassandra-3.8
Commit: 6528fbf250fdd2c75c8f9464e279628234a5fef4
Parents: e6dd152
Author: Alex Petrov <ol...@gmail.com>
Authored: Wed Apr 20 14:57:52 2016 +0200
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Mon Aug 15 18:31:45 2016 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../org/apache/cassandra/config/CFMetaData.java |   7 +-
 .../apache/cassandra/cql3/ColumnIdentifier.java |   2 +-
 src/java/org/apache/cassandra/cql3/Cql.g        |  11 +-
 .../cql3/statements/AlterTableStatement.java    |   8 +-
 .../apache/cassandra/db/ColumnFamilyStore.java  |  23 +
 .../db/ColumnFamilyStoreCQLHelper.java          | 442 ++++++++++++
 .../org/apache/cassandra/db/Directories.java    |   6 +
 .../unit/org/apache/cassandra/SchemaLoader.java |   5 +
 .../cql3/validation/operations/AlterTest.java   |  71 ++
 .../db/ColumnFamilyStoreCQLHelperTest.java      | 679 +++++++++++++++++++
 .../schema/LegacySchemaMigratorTest.java        |   3 +-
 12 files changed, 1248 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 7ba8370..a202755 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.9
+ * Add schema to snapshot manifest, add USING TIMESTAMP clause to ALTER TABLE statements (CASSANDRA-7190)
  * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
  * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
  * Backport CASSANDRA-12002 (CASSANDRA-12177)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/config/CFMetaData.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java b/src/java/org/apache/cassandra/config/CFMetaData.java
index 5678ada..c0f8d2d 100644
--- a/src/java/org/apache/cassandra/config/CFMetaData.java
+++ b/src/java/org/apache/cassandra/config/CFMetaData.java
@@ -954,9 +954,12 @@ public final class CFMetaData
         return removed;
     }
 
-    public void recordColumnDrop(ColumnDefinition def)
+    /**
+     * Adds the column definition as a dropped column, recording the drop with the provided timestamp.
+     */
+    public void recordColumnDrop(ColumnDefinition def, long deleteTimestamp)
     {
-        droppedColumns.put(def.name.bytes, new DroppedColumn(def.name.toString(), def.type, FBUtilities.timestampMicros()));
+        droppedColumns.put(def.name.bytes, new DroppedColumn(def.name.toString(), def.type, deleteTimestamp));
     }
 
     public void renameColumn(ColumnIdentifier from, ColumnIdentifier to) throws InvalidRequestException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
index 93734e9..afb65e1 100644
--- a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
+++ b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
@@ -328,7 +328,7 @@ public class ColumnIdentifier extends Selectable implements IMeasurableMemory, C
         }
     }
 
-    static String maybeQuote(String text)
+    public static String maybeQuote(String text)
     {
         if (UNQUOTED_IDENTIFIER.matcher(text).matches())
             return text;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g
index 453a03d..d50b979 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -818,18 +818,21 @@ alterTableStatement returns [AlterTableStatement expr]
         TableAttributes attrs = new TableAttributes();
         Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames = new HashMap<ColumnIdentifier.Raw, ColumnIdentifier.Raw>();
         boolean isStatic = false;
+        Long dropTimestamp = null;
     }
     : K_ALTER K_COLUMNFAMILY cf=columnFamilyName
           ( K_ALTER id=cident K_TYPE v=comparatorType { type = AlterTableStatement.Type.ALTER; }
           | K_ADD   id=cident v=comparatorType ({ isStatic=true; } K_STATIC)? { type = AlterTableStatement.Type.ADD; }
-          | K_DROP  id=cident                         { type = AlterTableStatement.Type.DROP; }
-          | K_WITH  properties[attrs]                 { type = AlterTableStatement.Type.OPTS; }
-          | K_RENAME                                  { type = AlterTableStatement.Type.RENAME; }
+          | K_DROP  id=cident                               { type = AlterTableStatement.Type.DROP; }
+          | K_DROP  id=cident K_USING K_TIMESTAMP t=INTEGER { type = AlterTableStatement.Type.DROP;
+                                                              dropTimestamp = Long.parseLong(Constants.Literal.integer($t.text).getText()); }
+          | K_WITH  properties[attrs]                       { type = AlterTableStatement.Type.OPTS; }
+          | K_RENAME                                        { type = AlterTableStatement.Type.RENAME; }
                id1=cident K_TO toId1=cident { renames.put(id1, toId1); }
                ( K_AND idn=cident K_TO toIdn=cident { renames.put(idn, toIdn); } )*
           )
     {
-        $expr = new AlterTableStatement(cf, type, id, v, attrs, renames, isStatic);
+        $expr = new AlterTableStatement(cf, type, id, v, attrs, renames, isStatic, dropTimestamp);
     }
     ;
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
index 381971f..d64e541 100644
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
@@ -41,6 +41,7 @@ import org.apache.cassandra.schema.TableParams;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
 import org.apache.cassandra.transport.Event;
+import org.apache.cassandra.utils.FBUtilities;
 
 import static org.apache.cassandra.thrift.ThriftValidation.validateColumnFamily;
 
@@ -57,6 +58,7 @@ public class AlterTableStatement extends SchemaAlteringStatement
     private final TableAttributes attrs;
     private final Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames;
     private final boolean isStatic; // Only for ALTER ADD
+    private final long deleteTimestamp;
 
     public AlterTableStatement(CFName name,
                                Type type,
@@ -64,7 +66,8 @@ public class AlterTableStatement extends SchemaAlteringStatement
                                CQL3Type.Raw validator,
                                TableAttributes attrs,
                                Map<ColumnIdentifier.Raw, ColumnIdentifier.Raw> renames,
-                               boolean isStatic)
+                               boolean isStatic,
+                               Long deleteTimestamp)
     {
         super(name);
         this.oType = type;
@@ -73,6 +76,7 @@ public class AlterTableStatement extends SchemaAlteringStatement
         this.attrs = attrs;
         this.renames = renames;
         this.isStatic = isStatic;
+        this.deleteTimestamp = deleteTimestamp == null ? FBUtilities.timestampMicros() : deleteTimestamp;
     }
 
     public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
@@ -238,7 +242,7 @@ public class AlterTableStatement extends SchemaAlteringStatement
                         }
                         assert toDelete != null;
                         cfm.removeColumnDefinition(toDelete);
-                        cfm.recordColumnDrop(toDelete);
+                        cfm.recordColumnDrop(toDelete, deleteTimestamp);
                         break;
                 }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index 6c02909..fb3665b 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -1627,6 +1627,8 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
                 }
 
                 writeSnapshotManifest(filesJSONArr, snapshotName);
+                if (!Schema.SYSTEM_KEYSPACE_NAMES.contains(metadata.ksName) && !Schema.REPLICATED_SYSTEM_KEYSPACE_NAMES.contains(metadata.ksName))
+                    writeSnapshotSchema(snapshotName);
             }
         }
         if (ephemeral)
@@ -1656,6 +1658,27 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
         }
     }
 
+    private void writeSnapshotSchema(final String snapshotName)
+    {
+        final File schemaFile = getDirectories().getSnapshotSchemaFile(snapshotName);
+
+        try
+        {
+            if (!schemaFile.getParentFile().exists())
+                schemaFile.getParentFile().mkdirs();
+
+            try (PrintStream out = new PrintStream(schemaFile))
+            {
+                for (String s: ColumnFamilyStoreCQLHelper.dumpReCreateStatements(metadata))
+                    out.println(s);
+            }
+        }
+        catch (IOException e)
+        {
+            throw new FSWriteError(e, schemaFile);
+        }
+    }
+
     private void createEphemeralSnapshotMarkerFile(final String snapshot)
     {
         final File ephemeralSnapshotMarker = getDirectories().getNewEphemeralSnapshotMarkerFile(snapshot);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java b/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java
new file mode 100644
index 0000000..f9d6897
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStoreCQLHelper.java
@@ -0,0 +1,442 @@
+/*
+ * Licensed to the Apache Softw\u2264are 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 java.util.*;
+import java.util.concurrent.atomic.*;
+import java.util.function.*;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Iterables;
+
+import org.apache.cassandra.config.*;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.statements.*;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.utils.*;
+
+/**
+ * Helper methods to represent CFMetadata and related objects in CQL format
+ */
+public class ColumnFamilyStoreCQLHelper
+{
+    public static List<String> dumpReCreateStatements(CFMetaData metadata)
+    {
+        List<String> l = new ArrayList<>();
+        // Types come first, as table can't be created without them
+        l.addAll(ColumnFamilyStoreCQLHelper.getUserTypesAsCQL(metadata));
+        // Record re-create schema statements
+        l.add(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(metadata, true));
+        // Dropped columns (and re-additions)
+        l.addAll(ColumnFamilyStoreCQLHelper.getDroppedColumnsAsCQL(metadata));
+        // Indexes applied as last, since otherwise they may interfere with column drops / re-additions
+        l.addAll(ColumnFamilyStoreCQLHelper.getIndexesAsCQL(metadata));
+        return l;
+    }
+
+    private static List<ColumnDefinition> getClusteringColumns(CFMetaData metadata)
+    {
+        List<ColumnDefinition> cds = new ArrayList<>(metadata.clusteringColumns().size());
+
+        if (!metadata.isStaticCompactTable())
+            for (ColumnDefinition cd : metadata.clusteringColumns())
+                cds.add(cd);
+
+        return cds;
+    }
+
+    private static List<ColumnDefinition> getPartitionColumns(CFMetaData metadata)
+    {
+        List<ColumnDefinition> cds = new ArrayList<>(metadata.partitionColumns().size());
+
+        for (ColumnDefinition cd : metadata.partitionColumns().statics)
+            cds.add(cd);
+
+        if (metadata.isDense())
+        {
+            // remove an empty type
+            for (ColumnDefinition cd : metadata.partitionColumns().withoutStatics())
+                if (!cd.type.equals(EmptyType.instance))
+                    cds.add(cd);
+        }
+        // "regular" columns are not exposed for static compact tables
+        else if (!metadata.isStaticCompactTable())
+        {
+            for (ColumnDefinition cd : metadata.partitionColumns().withoutStatics())
+                cds.add(cd);
+        }
+
+        return cds;
+    }
+
+    /**
+     * Build a CQL String representation of Column Family Metadata
+     */
+    @VisibleForTesting
+    public static String getCFMetadataAsCQL(CFMetaData metadata, boolean includeDroppedColumns)
+    {
+        StringBuilder sb = new StringBuilder();
+        if (!isCqlCompatible(metadata))
+        {
+            sb.append(String.format("/*\nWarning: Table %s.%s omitted because it has constructs not compatible with CQL (was created via legacy API).\n",
+                                    metadata.ksName,
+                                    metadata.cfName));
+            sb.append("\nApproximate structure, for reference:");
+            sb.append("\n(this should not be used to reproduce this schema)\n\n");
+        }
+
+        sb.append("CREATE TABLE IF NOT EXISTS ");
+        sb.append(quoteIdentifier(metadata.ksName)).append('.').append(quoteIdentifier(metadata.cfName)).append(" (");
+
+        List<ColumnDefinition> partitionKeyColumns = metadata.partitionKeyColumns();
+        List<ColumnDefinition> clusteringColumns = getClusteringColumns(metadata);
+        List<ColumnDefinition> partitionColumns = getPartitionColumns(metadata);
+
+        Consumer<StringBuilder> cdCommaAppender = commaAppender("\n\t");
+        sb.append("\n\t");
+        for (ColumnDefinition cfd: partitionKeyColumns)
+        {
+            cdCommaAppender.accept(sb);
+            sb.append(toCQL(cfd));
+            if (partitionKeyColumns.size() == 1 && clusteringColumns.size() == 0)
+                sb.append(" PRIMARY KEY");
+        }
+
+        for (ColumnDefinition cfd: clusteringColumns)
+        {
+            cdCommaAppender.accept(sb);
+            sb.append(toCQL(cfd));
+        }
+
+        for (ColumnDefinition cfd: partitionColumns)
+        {
+            cdCommaAppender.accept(sb);
+            sb.append(toCQL(cfd, metadata.isStaticCompactTable()));
+        }
+
+        if (includeDroppedColumns)
+        {
+            for (Map.Entry<ByteBuffer, CFMetaData.DroppedColumn> entry: metadata.getDroppedColumns().entrySet())
+            {
+                if (metadata.getColumnDefinition(entry.getKey()) != null)
+                    continue;
+
+                CFMetaData.DroppedColumn droppedColumn = entry.getValue();
+                cdCommaAppender.accept(sb);
+                sb.append(quoteIdentifier(droppedColumn.name));
+                sb.append(' ');
+                sb.append(droppedColumn.type.asCQL3Type().toString());
+            }
+        }
+
+        if (clusteringColumns.size() > 0 || partitionKeyColumns.size() > 1)
+        {
+            sb.append(",\n\tPRIMARY KEY (");
+            if (partitionKeyColumns.size() > 1)
+            {
+                sb.append("(");
+                Consumer<StringBuilder> pkCommaAppender = commaAppender(" ");
+                for (ColumnDefinition cfd : partitionKeyColumns)
+                {
+                    pkCommaAppender.accept(sb);
+                    sb.append(quoteIdentifier(cfd.name.toString()));
+                }
+                sb.append(")");
+            }
+            else
+            {
+                sb.append(quoteIdentifier(partitionKeyColumns.get(0).name.toString()));
+            }
+
+            for (ColumnDefinition cfd : metadata.clusteringColumns())
+                sb.append(", ").append(quoteIdentifier(cfd.name.toString()));
+
+            sb.append(')');
+        }
+        sb.append(")\n\t");
+        sb.append("WITH ");
+
+        sb.append("ID = ").append(metadata.cfId).append("\n\tAND ");
+
+        if (metadata.isCompactTable())
+            sb.append("COMPACT STORAGE\n\tAND ");
+
+        if (clusteringColumns.size() > 0)
+        {
+            sb.append("CLUSTERING ORDER BY (");
+
+            Consumer<StringBuilder> cOrderCommaAppender = commaAppender(" ");
+            for (ColumnDefinition cd : clusteringColumns)
+            {
+                cOrderCommaAppender.accept(sb);
+                sb.append(quoteIdentifier(cd.name.toString())).append(' ').append(cd.clusteringOrder().toString());
+            }
+            sb.append(")\n\tAND ");
+        }
+
+        sb.append(toCQL(metadata.params));
+        sb.append(";");
+
+        if (!isCqlCompatible(metadata))
+        {
+            sb.append("\n*/");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Build a CQL String representation of User Types used in the given Column Family.
+     *
+     * Type order is ensured as types are built incrementally: from the innermost (most nested)
+     * to the outermost.
+     */
+    @VisibleForTesting
+    public static List<String> getUserTypesAsCQL(CFMetaData metadata)
+    {
+        List<UserType> types = new ArrayList<>();
+        Set<UserType> typeSet = new HashSet<>();
+        for (ColumnDefinition cd: Iterables.concat(metadata.partitionKeyColumns(), metadata.clusteringColumns(), metadata.partitionColumns()))
+        {
+            AbstractType type = cd.type;
+            if (type instanceof UserType)
+                resolveUserType((UserType) type, typeSet, types);
+        }
+
+        List<String> typeStrings = new ArrayList<>();
+        for (UserType type: types)
+            typeStrings.add(toCQL(type));
+        return typeStrings;
+    }
+
+    /**
+     * Build a CQL String representation of Dropped Columns in the given Column Family.
+     *
+     * If the column was dropped once, but is now re-created `ADD` will be appended accordingly.
+     */
+    @VisibleForTesting
+    public static List<String> getDroppedColumnsAsCQL(CFMetaData metadata)
+    {
+        List<String> droppedColumns = new ArrayList<>();
+
+        for (Map.Entry<ByteBuffer, CFMetaData.DroppedColumn> entry: metadata.getDroppedColumns().entrySet())
+        {
+            CFMetaData.DroppedColumn column = entry.getValue();
+            droppedColumns.add(toCQLDrop(metadata.ksName, metadata.cfName, column));
+            if (metadata.getColumnDefinition(entry.getKey()) != null)
+                droppedColumns.add(toCQLAdd(metadata.ksName, metadata.cfName, metadata.getColumnDefinition(entry.getKey())));
+        }
+
+        return droppedColumns;
+    }
+
+    /**
+     * Build a CQL String representation of Indexes on columns in the given Column Family
+     */
+    @VisibleForTesting
+    public static List<String> getIndexesAsCQL(CFMetaData metadata)
+    {
+        List<String> indexes = new ArrayList<>();
+        for (IndexMetadata indexMetadata: metadata.getIndexes())
+            indexes.add(toCQL(metadata.ksName, metadata.cfName, indexMetadata));
+        return indexes;
+    }
+
+    private static String toCQL(String keyspace, String cf, IndexMetadata indexMetadata)
+    {
+        if (indexMetadata.isCustom())
+        {
+            Map<String, String> options = new HashMap<>();
+            indexMetadata.options.forEach((k, v) -> {
+                if (!k.equals(IndexTarget.TARGET_OPTION_NAME) && !k.equals(IndexTarget.CUSTOM_INDEX_OPTION_NAME))
+                    options.put(k, v);
+            });
+
+            return String.format("CREATE CUSTOM INDEX %s ON %s.%s (%s) USING '%s'%s;",
+                                 quoteIdentifier(indexMetadata.name),
+                                 quoteIdentifier(keyspace),
+                                 quoteIdentifier(cf),
+                                 indexMetadata.options.get(IndexTarget.TARGET_OPTION_NAME),
+                                 indexMetadata.options.get(IndexTarget.CUSTOM_INDEX_OPTION_NAME),
+                                 options.isEmpty() ? "" : " WITH OPTIONS " + toCQL(options));
+        }
+        else
+        {
+            return String.format("CREATE INDEX %s ON %s.%s (%s);",
+                                 quoteIdentifier(indexMetadata.name),
+                                 quoteIdentifier(keyspace),
+                                 quoteIdentifier(cf),
+                                 indexMetadata.options.get(IndexTarget.TARGET_OPTION_NAME));
+        }
+    }
+    private static String toCQL(UserType userType)
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append(String.format("CREATE TYPE %s.%s(",
+                                quoteIdentifier(userType.keyspace),
+                                quoteIdentifier(userType.getNameAsString())));
+
+        Consumer<StringBuilder> commaAppender = commaAppender(" ");
+        for (int i = 0; i < userType.size(); i++)
+        {
+            commaAppender.accept(sb);
+            sb.append(String.format("%s %s",
+                                    userType.fieldNameAsString(i),
+                                    userType.fieldType(i).asCQL3Type()));
+        }
+        sb.append(");");
+        return sb.toString();
+    }
+
+    private static String toCQL(TableParams tableParams)
+    {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append("bloom_filter_fp_chance = ").append(tableParams.bloomFilterFpChance);
+        builder.append("\n\tAND dclocal_read_repair_chance = ").append(tableParams.dcLocalReadRepairChance);
+        builder.append("\n\tAND crc_check_chance = ").append(tableParams.crcCheckChance);
+        builder.append("\n\tAND default_time_to_live = ").append(tableParams.defaultTimeToLive);
+        builder.append("\n\tAND gc_grace_seconds = ").append(tableParams.gcGraceSeconds);
+        builder.append("\n\tAND min_index_interval = ").append(tableParams.minIndexInterval);
+        builder.append("\n\tAND max_index_interval = ").append(tableParams.maxIndexInterval);
+        builder.append("\n\tAND memtable_flush_period_in_ms = ").append(tableParams.memtableFlushPeriodInMs);
+        builder.append("\n\tAND read_repair_chance = ").append(tableParams.readRepairChance);
+        builder.append("\n\tAND speculative_retry = '").append(tableParams.speculativeRetry).append("'");
+        builder.append("\n\tAND comment = ").append(singleQuote(tableParams.comment));
+        builder.append("\n\tAND caching = ").append(toCQL(tableParams.caching.asMap()));
+        builder.append("\n\tAND compaction = ").append(toCQL(tableParams.compaction.asMap()));
+        builder.append("\n\tAND compression = ").append(toCQL(tableParams.compression.asMap()));
+
+        builder.append("\n\tAND extensions = { ");
+        for (Map.Entry<String, ByteBuffer> entry : tableParams.extensions.entrySet())
+        {
+            builder.append(singleQuote(entry.getKey()));
+            builder.append(": ");
+            builder.append("0x" + ByteBufferUtil.bytesToHex(entry.getValue()));
+        }
+        builder.append(" }");
+        return builder.toString();
+    }
+
+    private static String toCQL(Map<?, ?> map)
+    {
+        StringBuilder builder = new StringBuilder("{ ");
+
+        boolean isFirst = true;
+        for (Map.Entry entry: map.entrySet())
+        {
+            if (isFirst)
+                isFirst = false;
+            else
+                builder.append(", ");
+            builder.append(singleQuote(entry.getKey().toString()));
+            builder.append(": ");
+            builder.append(singleQuote(entry.getValue().toString()));
+        }
+
+        builder.append(" }");
+        return builder.toString();
+    }
+
+    private static String toCQL(ColumnDefinition cd)
+    {
+        return toCQL(cd, false);
+    }
+
+    private static String toCQL(ColumnDefinition cd, boolean isStaticCompactTable)
+    {
+        return String.format("%s %s%s",
+                             quoteIdentifier(cd.name.toString()),
+                             cd.type.asCQL3Type().toString(),
+                             cd.isStatic() && !isStaticCompactTable ? " static" : "");
+    }
+
+    private static String toCQLAdd(String keyspace, String cf, ColumnDefinition cd)
+    {
+        return String.format("ALTER TABLE %s.%s ADD %s %s%s;",
+                             quoteIdentifier(keyspace),
+                             quoteIdentifier(cf),
+                             quoteIdentifier(cd.name.toString()),
+                             cd.type.asCQL3Type().toString(),
+                             cd.isStatic() ? " static" : "");
+    }
+
+    private static String toCQLDrop(String keyspace, String cf, CFMetaData.DroppedColumn droppedColumn)
+    {
+        return String.format("ALTER TABLE %s.%s DROP %s USING TIMESTAMP %s;",
+                             quoteIdentifier(keyspace),
+                             quoteIdentifier(cf),
+                             quoteIdentifier(droppedColumn.name),
+                             droppedColumn.droppedTime);
+    }
+
+    private static void resolveUserType(UserType type, Set<UserType> typeSet, List<UserType> types)
+    {
+        for (AbstractType subType: type.fieldTypes())
+            if (!typeSet.contains(subType) && subType instanceof UserType)
+                resolveUserType((UserType) subType, typeSet, types);
+
+        if (!typeSet.contains(type))
+        {
+            UserType t = type;
+            typeSet.add(t);
+            types.add(t);
+        }
+    }
+
+    private static String singleQuote(String s)
+    {
+        return String.format("'%s'", s.replaceAll("'", "''"));
+    }
+
+    private static Consumer<StringBuilder> commaAppender(String afterComma)
+    {
+        AtomicBoolean isFirst = new AtomicBoolean(true);
+        return new Consumer<StringBuilder>()
+        {
+            public void accept(StringBuilder stringBuilder)
+            {
+                if (!isFirst.getAndSet(false))
+                    stringBuilder.append(',').append(afterComma);
+            }
+        };
+    }
+
+    private static String quoteIdentifier(String id)
+    {
+        return ColumnIdentifier.maybeQuote(id);
+    }
+
+    /**
+     * Whether or not the given metadata is compatible / representable with CQL Language
+     */
+    public static boolean isCqlCompatible(CFMetaData metaData)
+    {
+        if (metaData.isSuper())
+            return false;
+
+        if (metaData.isCompactTable()
+            && metaData.partitionColumns().withoutStatics().size() > 1
+            && metaData.clusteringColumns().size() >= 1)
+            return false;
+
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/src/java/org/apache/cassandra/db/Directories.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/Directories.java b/src/java/org/apache/cassandra/db/Directories.java
index 877f984..30fe56d 100644
--- a/src/java/org/apache/cassandra/db/Directories.java
+++ b/src/java/org/apache/cassandra/db/Directories.java
@@ -477,6 +477,12 @@ public class Directories
         return new File(snapshotDir, "manifest.json");
     }
 
+    public File getSnapshotSchemaFile(String snapshotName)
+    {
+        File snapshotDir = getSnapshotDirectory(getDirectoryForNewSSTables(), snapshotName);
+        return new File(snapshotDir, "schema.cql");
+    }
+
     public File getNewEphemeralSnapshotMarkerFile(String snapshotName)
     {
         File snapshotDir = new File(getWriteableLocationAsFile(1L), join(SNAPSHOT_SUBDIR, snapshotName));

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/test/unit/org/apache/cassandra/SchemaLoader.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/SchemaLoader.java b/test/unit/org/apache/cassandra/SchemaLoader.java
index 5d720c4..dd4884c 100644
--- a/test/unit/org/apache/cassandra/SchemaLoader.java
+++ b/test/unit/org/apache/cassandra/SchemaLoader.java
@@ -264,6 +264,11 @@ public class SchemaLoader
         MigrationManager.announceNewKeyspace(KeyspaceMetadata.create(name, params, Tables.of(tables)), true);
     }
 
+    public static void createKeyspace(String name, KeyspaceParams params, Tables tables, Types types)
+    {
+        MigrationManager.announceNewKeyspace(KeyspaceMetadata.create(name, params, tables, Views.none(), types, Functions.none()), true);
+    }
+
     public static ColumnDefinition integerColumn(String ksName, String cfName)
     {
         return new ColumnDefinition(ksName,

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index 509aeac..bcd6587 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@ -99,6 +99,77 @@ public class AlterTest extends CQLTester
     }
 
     @Test
+    public void testDropWithTimestamp() throws Throwable
+    {
+        createTable("CREATE TABLE %s (id int, c1 int, v1 int, todrop int, PRIMARY KEY (id, c1));");
+        for (int i = 0; i < 5; i++)
+            execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, i, i, i, 10000L * i);
+
+        // flush is necessary since otherwise the values of `todrop` will get discarded during
+        // alter statement
+        flush();
+        execute("ALTER TABLE %s DROP todrop USING TIMESTAMP 20000;");
+        execute("ALTER TABLE %s ADD todrop int;");
+        execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, 100, 100, 100, 30000L);
+        assertRows(execute("SELECT id, c1, v1, todrop FROM %s"),
+                   row(1, 0, 0, null),
+                   row(1, 1, 1, null),
+                   row(1, 2, 2, null),
+                   row(1, 3, 3, 3),
+                   row(1, 4, 4, 4),
+                   row(1, 100, 100, 100));
+    }
+
+    @Test
+    public void testDropStaticWithTimestamp() throws Throwable
+    {
+        createTable("CREATE TABLE %s (id int, c1 int, v1 int, todrop int static, PRIMARY KEY (id, c1));");
+        for (int i = 0; i < 5; i++)
+            execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, i, i, i, 10000L * i);
+
+        // flush is necessary since otherwise the values of `todrop` will get discarded during
+        // alter statement
+        flush();
+        execute("ALTER TABLE %s DROP todrop USING TIMESTAMP 20000;");
+        execute("ALTER TABLE %s ADD todrop int static;");
+        execute("INSERT INTO %s (id, c1, v1, todrop) VALUES (?, ?, ?, ?) USING TIMESTAMP ?", 1, 100, 100, 100, 30000L);
+        // static column value with largest timestmap will be available again
+        assertRows(execute("SELECT id, c1, v1, todrop FROM %s"),
+                   row(1, 0, 0, 4),
+                   row(1, 1, 1, 4),
+                   row(1, 2, 2, 4),
+                   row(1, 3, 3, 4),
+                   row(1, 4, 4, 4),
+                   row(1, 100, 100, 4));
+    }
+
+    @Test
+    public void testDropMultipleWithTimestamp() throws Throwable
+    {
+        createTable("CREATE TABLE %s (id int, c1 int, v1 int, todrop1 int, todrop2 int, PRIMARY KEY (id, c1));");
+        for (int i = 0; i < 5; i++)
+            execute("INSERT INTO %s (id, c1, v1, todrop1, todrop2) VALUES (?, ?, ?, ?, ?) USING TIMESTAMP ?", 1, i, i, i, i, 10000L * i);
+
+        // flush is necessary since otherwise the values of `todrop1` and `todrop2` will get discarded during
+        // alter statement
+        flush();
+        execute("ALTER TABLE %s DROP todrop1 USING TIMESTAMP 20000;");
+        execute("ALTER TABLE %s DROP todrop2 USING TIMESTAMP 20000;");
+        execute("ALTER TABLE %s ADD todrop1 int;");
+        execute("ALTER TABLE %s ADD todrop2 int;");
+
+        execute("INSERT INTO %s (id, c1, v1, todrop1, todrop2) VALUES (?, ?, ?, ?, ?) USING TIMESTAMP ?", 1, 100, 100, 100, 100, 40000L);
+        assertRows(execute("SELECT id, c1, v1, todrop1, todrop2 FROM %s"),
+                   row(1, 0, 0, null, null),
+                   row(1, 1, 1, null, null),
+                   row(1, 2, 2, null, null),
+                   row(1, 3, 3, 3, 3),
+                   row(1, 4, 4, 4, 4),
+                   row(1, 100, 100, 100, 100));
+    }
+
+
+    @Test
     public void testChangeStrategyWithUnquotedAgrument() throws Throwable
     {
         createTable("CREATE TABLE %s (id text PRIMARY KEY);");

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java
new file mode 100644
index 0000000..c2e5cb7
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreCQLHelperTest.java
@@ -0,0 +1,679 @@
+/*
+ * 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.io.FileReader;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.*;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.cassandra.*;
+import org.apache.cassandra.config.*;
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.statements.*;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.schema.*;
+import org.apache.cassandra.utils.*;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ColumnFamilyStoreCQLHelperTest extends CQLTester
+{
+    @Before
+    public void defineSchema() throws ConfigurationException
+    {
+        SchemaLoader.prepareServer();
+    }
+
+    @Test
+    public void testUserTypesCQL()
+    {
+        String keyspace = "cql_test_keyspace_user_types";
+        String table = "test_table_user_types";
+
+        UserType typeA = new UserType(keyspace, ByteBufferUtil.bytes("a"),
+                                      Arrays.asList(ByteBufferUtil.bytes("a1"),
+                                                    ByteBufferUtil.bytes("a2"),
+                                                    ByteBufferUtil.bytes("a3")),
+                                      Arrays.asList(IntegerType.instance,
+                                                    IntegerType.instance,
+                                                    IntegerType.instance));
+
+        UserType typeB = new UserType(keyspace, ByteBufferUtil.bytes("b"),
+                                      Arrays.asList(ByteBufferUtil.bytes("b1"),
+                                                    ByteBufferUtil.bytes("b2"),
+                                                    ByteBufferUtil.bytes("b3")),
+                                      Arrays.asList(typeA,
+                                                    typeA,
+                                                    typeA));
+
+        UserType typeC = new UserType(keyspace, ByteBufferUtil.bytes("c"),
+                                      Arrays.asList(ByteBufferUtil.bytes("c1"),
+                                                    ByteBufferUtil.bytes("c2"),
+                                                    ByteBufferUtil.bytes("c3")),
+                                      Arrays.asList(typeB,
+                                                    typeB,
+                                                    typeB));
+
+        CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+                                           .addPartitionKey("pk1", IntegerType.instance)
+                                           .addClusteringColumn("ck1", IntegerType.instance)
+                                           .addRegularColumn("reg1", typeC)
+                                           .addRegularColumn("reg2", ListType.getInstance(IntegerType.instance, false))
+                                           .addRegularColumn("reg3", MapType.getInstance(AsciiType.instance, IntegerType.instance, true))
+                                           .build();
+
+        SchemaLoader.createKeyspace(keyspace,
+                                    KeyspaceParams.simple(1),
+                                    Tables.of(cfm),
+                                    Types.of(typeA, typeB, typeC));
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+        assertEquals(ImmutableList.of("CREATE TYPE cql_test_keyspace_user_types.a(a1 varint, a2 varint, a3 varint);",
+                                      "CREATE TYPE cql_test_keyspace_user_types.b(b1 frozen<a>, b2 frozen<a>, b3 frozen<a>);",
+                                      "CREATE TYPE cql_test_keyspace_user_types.c(c1 frozen<b>, c2 frozen<b>, c3 frozen<b>);"),
+                     ColumnFamilyStoreCQLHelper.getUserTypesAsCQL(cfs.metadata));
+    }
+
+    @Test
+    public void testDroppedColumnsCQL()
+    {
+        String keyspace = "cql_test_keyspace_dropped_columns";
+        String table = "test_table_dropped_columns";
+
+        CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+                                           .addPartitionKey("pk1", IntegerType.instance)
+                                           .addClusteringColumn("ck1", IntegerType.instance)
+                                           .addRegularColumn("reg1", IntegerType.instance)
+                                           .addRegularColumn("reg2", IntegerType.instance)
+                                           .addRegularColumn("reg3", IntegerType.instance)
+                                           .build();
+
+
+        ColumnDefinition reg1 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1"));
+        ColumnDefinition reg2 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg2"));
+        ColumnDefinition reg3 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg3"));
+
+        cfm.removeColumnDefinition(reg1);
+        cfm.removeColumnDefinition(reg2);
+        cfm.removeColumnDefinition(reg3);
+
+        cfm.recordColumnDrop(reg1, 10000);
+        cfm.recordColumnDrop(reg2, 20000);
+        cfm.recordColumnDrop(reg3, 30000);
+
+        SchemaLoader.createKeyspace(keyspace,
+                                    KeyspaceParams.simple(1),
+                                    cfm);
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+        assertEquals(ImmutableList.of("ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg1 USING TIMESTAMP 10000;",
+                                      "ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg3 USING TIMESTAMP 30000;",
+                                      "ALTER TABLE cql_test_keyspace_dropped_columns.test_table_dropped_columns DROP reg2 USING TIMESTAMP 20000;"),
+                     ColumnFamilyStoreCQLHelper.getDroppedColumnsAsCQL(cfs.metadata));
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS cql_test_keyspace_dropped_columns.test_table_dropped_columns (\n" +
+        "\tpk1 varint,\n" +
+        "\tck1 varint,\n" +
+        "\treg1 varint,\n" +
+        "\treg3 varint,\n" +
+        "\treg2 varint,\n" +
+        "\tPRIMARY KEY (pk1, ck1))"));
+    }
+
+    @Test
+    public void testReaddedColumns()
+    {
+        String keyspace = "cql_test_keyspace_readded_columns";
+        String table = "test_table_readded_columns";
+
+        CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+                                           .addPartitionKey("pk1", IntegerType.instance)
+                                           .addClusteringColumn("ck1", IntegerType.instance)
+                                           .addRegularColumn("reg1", IntegerType.instance)
+                                           .addStaticColumn("reg2", IntegerType.instance)
+                                           .addRegularColumn("reg3", IntegerType.instance)
+                                           .build();
+
+        ColumnDefinition reg1 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1"));
+        ColumnDefinition reg2 = cfm.getColumnDefinition(ByteBufferUtil.bytes("reg2"));
+
+        cfm.removeColumnDefinition(reg1);
+        cfm.removeColumnDefinition(reg2);
+
+        cfm.recordColumnDrop(reg1, 10000);
+        cfm.recordColumnDrop(reg2, 20000);
+
+        cfm.addColumnDefinition(reg1);
+        cfm.addColumnDefinition(reg2);
+
+        SchemaLoader.createKeyspace(keyspace,
+                                    KeyspaceParams.simple(1),
+                                    cfm);
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+        // when re-adding, column is present in CREATE, then in DROP and then in ADD again, to record DROP with a proper timestamp
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS cql_test_keyspace_readded_columns.test_table_readded_columns (\n" +
+        "\tpk1 varint,\n" +
+        "\tck1 varint,\n" +
+        "\treg2 varint static,\n" +
+        "\treg1 varint,\n" +
+        "\treg3 varint,\n" +
+        "\tPRIMARY KEY (pk1, ck1))"));
+
+        assertEquals(ImmutableList.of("ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns DROP reg1 USING TIMESTAMP 10000;",
+                                      "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns ADD reg1 varint;",
+                                      "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns DROP reg2 USING TIMESTAMP 20000;",
+                                      "ALTER TABLE cql_test_keyspace_readded_columns.test_table_readded_columns ADD reg2 varint static;"),
+                     ColumnFamilyStoreCQLHelper.getDroppedColumnsAsCQL(cfs.metadata));
+    }
+
+    @Test
+    public void testCfmColumnsCQL()
+    {
+        String keyspace = "cql_test_keyspace_create_table";
+        String table = "test_table_create_table";
+
+        CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+                                           .addPartitionKey("pk1", IntegerType.instance)
+                                           .addPartitionKey("pk2", AsciiType.instance)
+                                           .addClusteringColumn("ck1", ReversedType.getInstance(IntegerType.instance))
+                                           .addClusteringColumn("ck2", IntegerType.instance)
+                                           .addStaticColumn("st1", AsciiType.instance)
+                                           .addRegularColumn("reg1", AsciiType.instance)
+                                           .addRegularColumn("reg2", ListType.getInstance(IntegerType.instance, false))
+                                           .addRegularColumn("reg3", MapType.getInstance(AsciiType.instance, IntegerType.instance, true))
+                                           .build();
+
+        SchemaLoader.createKeyspace(keyspace,
+                                    KeyspaceParams.simple(1),
+                                    cfm);
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS cql_test_keyspace_create_table.test_table_create_table (\n" +
+        "\tpk1 varint,\n" +
+        "\tpk2 ascii,\n" +
+        "\tck1 varint,\n" +
+        "\tck2 varint,\n" +
+        "\tst1 ascii static,\n" +
+        "\treg1 ascii,\n" +
+        "\treg2 frozen<list<varint>>,\n" +
+        "\treg3 map<ascii, varint>,\n" +
+        "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" +
+        "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+        "\tAND CLUSTERING ORDER BY (ck1 DESC, ck2 ASC)"));
+    }
+
+    @Test
+    public void testCfmCompactStorageCQL()
+    {
+        String keyspace = "cql_test_keyspace_compact";
+        String table = "test_table_compact";
+
+        CFMetaData cfm = CFMetaData.Builder.createDense(keyspace, table, true, false)
+                                           .addPartitionKey("pk1", IntegerType.instance)
+                                           .addPartitionKey("pk2", AsciiType.instance)
+                                           .addClusteringColumn("ck1", ReversedType.getInstance(IntegerType.instance))
+                                           .addClusteringColumn("ck2", IntegerType.instance)
+                                           .addRegularColumn("reg", IntegerType.instance)
+                                           .build();
+
+        SchemaLoader.createKeyspace(keyspace,
+                                    KeyspaceParams.simple(1),
+                                    cfm);
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS cql_test_keyspace_compact.test_table_compact (\n" +
+        "\tpk1 varint,\n" +
+        "\tpk2 ascii,\n" +
+        "\tck1 varint,\n" +
+        "\tck2 varint,\n" +
+        "\treg varint,\n" +
+        "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" +
+        "\tWITH ID = " + cfm.cfId + "\n" +
+        "\tAND COMPACT STORAGE\n" +
+        "\tAND CLUSTERING ORDER BY (ck1 DESC, ck2 ASC)"));
+    }
+
+    @Test
+    public void testCfmCounterCQL()
+    {
+        String keyspace = "cql_test_keyspace_counter";
+        String table = "test_table_counter";
+
+        CFMetaData cfm = CFMetaData.Builder.createDense(keyspace, table, true, true)
+                                           .addPartitionKey("pk1", IntegerType.instance)
+                                           .addPartitionKey("pk2", AsciiType.instance)
+                                           .addClusteringColumn("ck1", ReversedType.getInstance(IntegerType.instance))
+                                           .addClusteringColumn("ck2", IntegerType.instance)
+                                           .addRegularColumn("cnt", CounterColumnType.instance)
+                                           .build();
+
+        SchemaLoader.createKeyspace(keyspace,
+                                    KeyspaceParams.simple(1),
+                                    cfm);
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS cql_test_keyspace_counter.test_table_counter (\n" +
+        "\tpk1 varint,\n" +
+        "\tpk2 ascii,\n" +
+        "\tck1 varint,\n" +
+        "\tck2 varint,\n" +
+        "\tcnt counter,\n" +
+        "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" +
+        "\tWITH ID = " + cfm.cfId + "\n" +
+        "\tAND COMPACT STORAGE\n" +
+        "\tAND CLUSTERING ORDER BY (ck1 DESC, ck2 ASC)"));
+    }
+
+    @Test
+    public void testCfmOptionsCQL()
+    {
+        String keyspace = "cql_test_keyspace_options";
+        String table = "test_table_options";
+
+        CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+                                           .addPartitionKey("pk1", IntegerType.instance)
+                                           .addClusteringColumn("cl1", IntegerType.instance)
+                                           .addRegularColumn("reg1", AsciiType.instance)
+                                           .build();
+
+        cfm.recordColumnDrop(cfm.getColumnDefinition(ByteBuffer.wrap("reg1".getBytes())), FBUtilities.timestampMicros());
+        cfm.bloomFilterFpChance(1.0);
+        cfm.comment("comment");
+        cfm.compaction(CompactionParams.lcs(Collections.singletonMap("sstable_size_in_mb", "1")));
+        cfm.compression(CompressionParams.lz4(1 << 16));
+        cfm.dcLocalReadRepairChance(0.2);
+        cfm.crcCheckChance(0.3);
+        cfm.defaultTimeToLive(4);
+        cfm.gcGraceSeconds(5);
+        cfm.minIndexInterval(6);
+        cfm.maxIndexInterval(7);
+        cfm.memtableFlushPeriod(8);
+        cfm.readRepairChance(0.9);
+        cfm.speculativeRetry(SpeculativeRetryParam.always());
+        cfm.extensions(ImmutableMap.of("ext1",
+                                       ByteBuffer.wrap("val1".getBytes())));
+
+        SchemaLoader.createKeyspace(keyspace,
+                                    KeyspaceParams.simple(1),
+                                    cfm);
+        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).endsWith(
+        "AND bloom_filter_fp_chance = 1.0\n" +
+        "\tAND dclocal_read_repair_chance = 0.2\n" +
+        "\tAND crc_check_chance = 0.3\n" +
+        "\tAND default_time_to_live = 4\n" +
+        "\tAND gc_grace_seconds = 5\n" +
+        "\tAND min_index_interval = 6\n" +
+        "\tAND max_index_interval = 7\n" +
+        "\tAND memtable_flush_period_in_ms = 8\n" +
+        "\tAND read_repair_chance = 0.9\n" +
+        "\tAND speculative_retry = 'ALWAYS'\n" +
+        "\tAND comment = 'comment'\n" +
+        "\tAND caching = { 'keys': 'ALL', 'rows_per_partition': 'NONE' }\n" +
+        "\tAND compaction = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy', 'sstable_size_in_mb': '1' }\n" +
+        "\tAND compression = { 'chunk_length_in_kb': '64', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor' }\n" +
+        "\tAND extensions = { 'ext1': 0x76616c31 };"
+        ));
+    }
+
+    @Test
+    public void testCfmIndexJson()
+    {
+        String keyspace = "cql_test_keyspace_3";
+        String table = "test_table_3";
+
+        CFMetaData cfm = CFMetaData.Builder.create(keyspace, table)
+                                           .addPartitionKey("pk1", IntegerType.instance)
+                                           .addClusteringColumn("cl1", IntegerType.instance)
+                                           .addRegularColumn("reg1", AsciiType.instance)
+                                           .build();
+
+        cfm.indexes(cfm.getIndexes()
+                       .with(IndexMetadata.fromIndexTargets(cfm,
+                                                            Collections.singletonList(new IndexTarget(cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1")).name,
+                                                                                                      IndexTarget.Type.VALUES)),
+                                                            "indexName",
+                                                            IndexMetadata.Kind.COMPOSITES,
+                                                            Collections.emptyMap()))
+                       .with(IndexMetadata.fromIndexTargets(cfm,
+                                                            Collections.singletonList(new IndexTarget(cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1")).name,
+                                                                                                      IndexTarget.Type.KEYS)),
+                                                            "indexName2",
+                                                            IndexMetadata.Kind.COMPOSITES,
+                                                            Collections.emptyMap()))
+                       .with(IndexMetadata.fromIndexTargets(cfm,
+                                                            Collections.singletonList(new IndexTarget(cfm.getColumnDefinition(ByteBufferUtil.bytes("reg1")).name,
+                                                                                                      IndexTarget.Type.KEYS_AND_VALUES)),
+                                                            "indexName3",
+                                                            IndexMetadata.Kind.COMPOSITES,
+                                                            Collections.emptyMap())));
+
+
+        SchemaLoader.createKeyspace(keyspace,
+                                    KeyspaceParams.simple(1),
+                                    cfm);
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
+
+        assertEquals(ImmutableList.of("CREATE INDEX \"indexName\" ON cql_test_keyspace_3.test_table_3 (reg1);",
+                                      "CREATE INDEX \"indexName2\" ON cql_test_keyspace_3.test_table_3 (reg1);",
+                                      "CREATE INDEX \"indexName3\" ON cql_test_keyspace_3.test_table_3 (reg1);"),
+                     ColumnFamilyStoreCQLHelper.getIndexesAsCQL(cfs.metadata));
+    }
+
+    private final static String SNAPSHOT = "testsnapshot";
+
+    @Test
+    public void testSnapshot() throws Throwable
+    {
+        String typeA = createType("CREATE TYPE %s (a1 varint, a2 varint, a3 varint);");
+        String typeB = createType("CREATE TYPE %s (b1 frozen<" + typeA + ">, b2 frozen<" + typeA + ">, b3 frozen<" + typeA + ">);");
+        String typeC = createType("CREATE TYPE %s (c1 frozen<" + typeB + ">, c2 frozen<" + typeB + ">, c3 frozen<" + typeB + ">);");
+
+        String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+                                       "pk1 varint," +
+                                       "pk2 ascii," +
+                                       "ck1 varint," +
+                                       "ck2 varint," +
+                                       "reg1 frozen<" + typeC + ">," +
+                                       "reg2 int," +
+                                       "reg3 int," +
+                                       "PRIMARY KEY ((pk1, pk2), ck1, ck2)) WITH " +
+                                       "CLUSTERING ORDER BY (ck1 ASC, ck2 DESC);");
+
+        alterTable("ALTER TABLE %s DROP reg3 USING TIMESTAMP 10000;");
+        alterTable("ALTER TABLE %s ADD reg3 int;");
+
+        for (int i = 0; i < 10; i++)
+            execute("INSERT INTO %s (pk1, pk2, ck1, ck2, reg1, reg2) VALUES (?, ?, ?, ?, ?, ?)", i, i + 1, i + 2, i + 3, null, i + 5);
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+        cfs.snapshot(SNAPSHOT);
+
+        String schema = Files.toString(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT), Charset.defaultCharset());
+        assertTrue(schema.contains(String.format("CREATE TYPE %s.%s(a1 varint, a2 varint, a3 varint);", keyspace(), typeA)));
+        assertTrue(schema.contains(String.format("CREATE TYPE %s.%s(a1 varint, a2 varint, a3 varint);", keyspace(), typeA)));
+        assertTrue(schema.contains(String.format("CREATE TYPE %s.%s(b1 frozen<%s>, b2 frozen<%s>, b3 frozen<%s>);", keyspace(), typeB, typeA, typeA, typeA)));
+        assertTrue(schema.contains(String.format("CREATE TYPE %s.%s(c1 frozen<%s>, c2 frozen<%s>, c3 frozen<%s>);", keyspace(), typeC, typeB, typeB, typeB)));
+
+        schema = schema.substring(schema.indexOf("CREATE TABLE")); // trim to ensure order
+
+        assertTrue(schema.startsWith("CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+                                     "\tpk1 varint,\n" +
+                                     "\tpk2 ascii,\n" +
+                                     "\tck1 varint,\n" +
+                                     "\tck2 varint,\n" +
+                                     "\treg1 frozen<" + typeC + ">,\n" +
+                                     "\treg2 int,\n" +
+                                     "\treg3 int,\n" +
+                                     "\tPRIMARY KEY ((pk1, pk2), ck1, ck2))\n" +
+                                     "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+                                     "\tAND CLUSTERING ORDER BY (ck1 ASC, ck2 DESC)"));
+
+        schema = schema.substring(schema.indexOf("ALTER"));
+        assertTrue(schema.startsWith(String.format("ALTER TABLE %s.%s DROP reg3 USING TIMESTAMP 10000;", keyspace(), tableName)));
+        assertTrue(schema.contains(String.format("ALTER TABLE %s.%s ADD reg3 int;", keyspace(), tableName)));
+
+        JSONObject manifest = (JSONObject) new JSONParser().parse(new FileReader(cfs.getDirectories().getSnapshotManifestFile(SNAPSHOT)));
+        JSONArray files = (JSONArray) manifest.get("files");
+        Assert.assertEquals(1, files.size());
+    }
+
+    @Test
+    public void testSystemKsSnapshot() throws Throwable
+    {
+        ColumnFamilyStore cfs = Keyspace.open("system").getColumnFamilyStore("peers");
+        cfs.snapshot(SNAPSHOT);
+
+        Assert.assertTrue(cfs.getDirectories().getSnapshotManifestFile(SNAPSHOT).exists());
+        Assert.assertFalse(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT).exists());
+    }
+
+    @Test
+    public void testDroppedType() throws Throwable
+    {
+        String typeA = createType("CREATE TYPE %s (a1 varint, a2 varint, a3 varint);");
+        String typeB = createType("CREATE TYPE %s (b1 frozen<" + typeA + ">, b2 frozen<" + typeA + ">, b3 frozen<" + typeA + ">);");
+
+        String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+                                       "pk1 varint," +
+                                       "ck1 varint," +
+                                       "reg1 frozen<" + typeB + ">," +
+                                       "reg2 varint," +
+                                       "PRIMARY KEY (pk1, ck1));");
+
+        alterTable("ALTER TABLE %s DROP reg1 USING TIMESTAMP 10000;");
+
+        Runnable validate = () -> {
+            try
+            {
+                ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+                cfs.snapshot(SNAPSHOT);
+                String schema = Files.toString(cfs.getDirectories().getSnapshotSchemaFile(SNAPSHOT), Charset.defaultCharset());
+
+                // When both column and it's type are dropped, the type in column definition gets substituted with a tuple
+                assertTrue(schema.startsWith("CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+                                             "\tpk1 varint,\n" +
+                                             "\tck1 varint,\n" +
+                                             "\treg2 varint,\n" +
+                                             "\treg1 frozen<tuple<frozen<tuple<varint, varint, varint>>, frozen<tuple<varint, varint, varint>>, frozen<tuple<varint, varint, varint>>>>,\n" +
+                                             "\tPRIMARY KEY (pk1, ck1))"));
+                assertTrue(schema.contains("ALTER TABLE " + keyspace() + "." + tableName + " DROP reg1 USING TIMESTAMP 10000;"));
+            }
+            catch (Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        };
+
+        // Validate before and after the type drop
+        validate.run();
+        schemaChange("DROP TYPE " + keyspace() + "." + typeB);
+        schemaChange("DROP TYPE " + keyspace() + "." + typeA);
+        validate.run();
+    }
+
+    @Test
+    public void testDenseTable() throws Throwable
+    {
+        String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+                                       "pk1 varint PRIMARY KEY," +
+                                       "reg1 int)" +
+                                       " WITH COMPACT STORAGE");
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+        "\tpk1 varint PRIMARY KEY,\n" +
+        "\treg1 int)\n" +
+        "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+        "\tAND COMPACT STORAGE"));
+    }
+
+    @Test
+    public void testStaticCompactTable() throws Throwable
+    {
+        String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+                                       "pk1 varint PRIMARY KEY," +
+                                       "reg1 int," +
+                                       "reg2 int)" +
+                                       " WITH COMPACT STORAGE");
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+        "\tpk1 varint PRIMARY KEY,\n" +
+        "\treg1 int,\n" +
+        "\treg2 int)\n" +
+        "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+        "\tAND COMPACT STORAGE"));
+    }
+
+    @Test
+    public void testStaticCompactWithCounters() throws Throwable
+    {
+        String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+                                       "pk1 varint PRIMARY KEY," +
+                                       "reg1 counter," +
+                                       "reg2 counter)" +
+                                       " WITH COMPACT STORAGE");
+
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+        "\tpk1 varint PRIMARY KEY,\n" +
+        "\treg1 counter,\n" +
+        "\treg2 counter)\n" +
+        "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+        "\tAND COMPACT STORAGE"));
+    }
+
+    @Test
+    public void testDenseCompactTableWithoutRegulars() throws Throwable
+    {
+        String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+                                       "pk1 varint," +
+                                       "ck1 int," +
+                                       "PRIMARY KEY (pk1, ck1))" +
+                                       " WITH COMPACT STORAGE");
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+        "\tpk1 varint,\n" +
+        "\tck1 int,\n" +
+        "\tPRIMARY KEY (pk1, ck1))\n" +
+        "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+        "\tAND COMPACT STORAGE"));
+    }
+
+    @Test
+    public void testCompactDynamic() throws Throwable
+    {
+        String tableName = createTable("CREATE TABLE IF NOT EXISTS %s (" +
+                                       "pk1 varint," +
+                                       "ck1 int," +
+                                       "reg int," +
+                                       "PRIMARY KEY (pk1, ck1))" +
+                                       " WITH COMPACT STORAGE");
+
+        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(tableName);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS " + keyspace() + "." + tableName + " (\n" +
+        "\tpk1 varint,\n" +
+        "\tck1 int,\n" +
+        "\treg int,\n" +
+        "\tPRIMARY KEY (pk1, ck1))\n" +
+        "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+        "\tAND COMPACT STORAGE"));
+    }
+
+    @Test
+    public void testDynamicComposite() throws Throwable
+    {
+        Map<Byte, AbstractType<?>> aliases = new HashMap<>();
+        aliases.put((byte)'a', BytesType.instance);
+        aliases.put((byte)'b', BytesType.instance);
+        aliases.put((byte)'c', BytesType.instance);
+
+        String DYNAMIC_COMPOSITE = "dynamic_composite";
+        AbstractType<?> dynamicComposite = DynamicCompositeType.getInstance(aliases);
+
+        SchemaLoader.createKeyspace(DYNAMIC_COMPOSITE,
+                                    KeyspaceParams.simple(1),
+                                    SchemaLoader.denseCFMD(DYNAMIC_COMPOSITE, DYNAMIC_COMPOSITE, dynamicComposite));
+
+        ColumnFamilyStore cfs = Keyspace.open(DYNAMIC_COMPOSITE).getColumnFamilyStore(DYNAMIC_COMPOSITE);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "CREATE TABLE IF NOT EXISTS " + DYNAMIC_COMPOSITE + "." + DYNAMIC_COMPOSITE + " (\n" +
+        "\tkey ascii,\n" +
+        "\tcols 'org.apache.cassandra.db.marshal.DynamicCompositeType(a=>org.apache.cassandra.db.marshal.BytesType,b=>org.apache.cassandra.db.marshal.BytesType,c=>org.apache.cassandra.db.marshal.BytesType)',\n" +
+        "\tval ascii,\n" +
+        "\tPRIMARY KEY (key, cols))\n" +
+        "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+        "\tAND COMPACT STORAGE"));
+    }
+
+    @Test
+    public void superColumnFamilyTest() throws Throwable
+    {
+        final String KEYSPACE = "thrift_compact_table_with_supercolumns_test";
+        final String TABLE = "test_table_1";
+
+        CFMetaData cfm = CFMetaData.Builder.createSuper(KEYSPACE, TABLE, false)
+                                           .addPartitionKey("pk", BytesType.instance)
+                                           .addClusteringColumn("c1", AsciiType.instance)
+                                           .addClusteringColumn("c2", AsciiType.instance)
+                                           .addRegularColumn("", MapType.getInstance(Int32Type.instance, AsciiType.instance, true))
+                                           .build();
+
+        SchemaLoader.createKeyspace(KEYSPACE,
+                                    KeyspaceParams.simple(1),
+                                    cfm);
+
+        ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore(TABLE);
+
+        assertTrue(ColumnFamilyStoreCQLHelper.getCFMetadataAsCQL(cfs.metadata, true).startsWith(
+        "/*\n" +
+        "Warning: Table " + KEYSPACE + "." + TABLE + " omitted because it has constructs not compatible with CQL (was created via legacy API).\n\n" +
+        "Approximate structure, for reference:\n" +
+        "(this should not be used to reproduce this schema)\n\n" +
+        "CREATE TABLE IF NOT EXISTS " + KEYSPACE + "." + TABLE + " (\n" +
+        "\tpk blob,\n" +
+        "\tc1 ascii,\n" +
+        "\tc2 ascii,\n" +
+        "\t\"\" map<int, ascii>,\n" +
+        "\tPRIMARY KEY (pk, c1, c2))\n" +
+        "\tWITH ID = " + cfs.metadata.cfId + "\n" +
+        "\tAND COMPACT STORAGE"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6528fbf2/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
index feb2778..3676ca3 100644
--- a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
+++ b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
@@ -36,6 +36,7 @@ import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.marshal.*;
 import org.apache.cassandra.index.internal.CassandraIndex;
 import org.apache.cassandra.thrift.ThriftConversion;
+import org.apache.cassandra.utils.*;
 
 import static java.lang.String.format;
 import static junit.framework.Assert.assertEquals;
@@ -284,7 +285,7 @@ public class LegacySchemaMigratorTest
         for (String name : collectionColumnNames)
         {
             ColumnDefinition column = table.getColumnDefinition(bytes(name));
-            table.recordColumnDrop(column);
+            table.recordColumnDrop(column, FBUtilities.timestampMicros());
             table.removeColumnDefinition(column);
         }
 


[23/50] [abbrv] cassandra git commit: Merge branch cassandra-3.0 into cassandra-3.9

Posted by ms...@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/0c771a2d
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/0c771a2d
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/0c771a2d

Branch: refs/heads/cassandra-3.8
Commit: 0c771a2db1d94901b4c7b194b9b71a115a1dc828
Parents: f4c3e14 c85749e
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Aug 12 15:40:10 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Aug 12 15:40:55 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../apache/cassandra/service/StorageProxy.java  | 49 +++++++++++++-------
 2 files changed, 34 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/0c771a2d/CHANGES.txt
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0c771a2d/src/java/org/apache/cassandra/service/StorageProxy.java
----------------------------------------------------------------------


[42/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into cassandra-2.2

Posted by ms...@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>

[40/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into cassandra-2.2

Posted by ms...@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")));
+             }
+         }
+     }
  }


[07/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/2d769f0c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/2d769f0c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/2d769f0c

Branch: refs/heads/cassandra-3.8
Commit: 2d769f0c5721be06a7401c69f45cfddf4d8fc736
Parents: 8458e4e 465def8
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Wed Aug 10 11:12:18 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Wed Aug 10 11:12:18 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                             |  2 ++
 src/java/org/apache/cassandra/net/MessageOut.java       |  2 +-
 src/java/org/apache/cassandra/net/MessagingService.java | 10 ++++++++++
 3 files changed, 13 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/2d769f0c/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 8fbd774,86746ad..9f7631d
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -22,48 -17,6 +22,50 @@@ Merged from 3.0
     to connect with too low of a protocol version (CASSANDRA-11464)
   * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
   * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
 +Merged from 2.2:
 + * Fix hanging stream session (CASSANDRA-10992)
 + * Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
 + * Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
 + * 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)
 +Merged from 2.1:
 + * cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
 + * Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
 +
 +
 +3.8
 + * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
 + * Fix hdr logging for single operation workloads (CASSANDRA-12145)
 + * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
 + * Increase size of flushExecutor thread pool (CASSANDRA-12071)
 + * Partial revert of CASSANDRA-11971, cannot recycle buffer in SP.sendMessagesToNonlocalDC (CASSANDRA-11950)
 + * Upgrade netty to 4.0.39 (CASSANDRA-12032, CASSANDRA-12034)
 + * Improve details in compaction log message (CASSANDRA-12080)
 + * Allow unset values in CQLSSTableWriter (CASSANDRA-11911)
 + * Chunk cache to request compressor-compatible buffers if pool space is exhausted (CASSANDRA-11993)
 + * Remove DatabaseDescriptor dependencies from SequentialWriter (CASSANDRA-11579)
 + * Move skip_stop_words filter before stemming (CASSANDRA-12078)
 + * Support seek() in EncryptedFileSegmentInputStream (CASSANDRA-11957)
 + * SSTable tools mishandling LocalPartitioner (CASSANDRA-12002)
 + * When SEPWorker assigned work, set thread name to match pool (CASSANDRA-11966)
 + * Add cross-DC latency metrics (CASSANDRA-11596)
 + * Allow terms in selection clause (CASSANDRA-10783)
 + * Add bind variables to trace (CASSANDRA-11719)
 + * Switch counter shards' clock to timestamps (CASSANDRA-9811)
 + * Introduce HdrHistogram and response/service/wait separation to stress tool (CASSANDRA-11853)
 + * entry-weighers in QueryProcessor should respect partitionKeyBindIndexes field (CASSANDRA-11718)
 + * Support older ant versions (CASSANDRA-11807)
 + * Estimate compressed on disk size when deciding if sstable size limit reached (CASSANDRA-11623)
 + * cassandra-stress profiles should support case sensitive schemas (CASSANDRA-11546)
 + * Remove DatabaseDescriptor dependency from FileUtils (CASSANDRA-11578)
 + * Faster streaming (CASSANDRA-9766)
 + * Add prepared query parameter to trace for "Execute CQL3 prepared query" session (CASSANDRA-11425)
 + * Add repaired percentage metric (CASSANDRA-11503)
 + * Add Change-Data-Capture (CASSANDRA-8844)
 +Merged from 3.0:
++ * Fix potential bad messaging service message for paged range reads
++   within mixed-version 3.x clusters (CASSANDRA-12249)
   * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
   * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
   * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2d769f0c/src/java/org/apache/cassandra/net/MessageOut.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2d769f0c/src/java/org/apache/cassandra/net/MessagingService.java
----------------------------------------------------------------------


[32/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2' into cassandra-3.0

Posted by ms...@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/74ac3cef
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/74ac3cef
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/74ac3cef

Branch: refs/heads/cassandra-3.8
Commit: 74ac3cef1068d46046560b4bf80fd1f47a439510
Parents: 5fdb90e 315356c
Author: Jason Brown <ja...@gmail.com>
Authored: Sun Aug 14 08:44:04 2016 -0700
Committer: Jason Brown <ja...@gmail.com>
Committed: Sun Aug 14 08:44:32 2016 -0700

----------------------------------------------------------------------
 .../unit/org/apache/cassandra/streaming/StreamingTransferTest.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/74ac3cef/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
----------------------------------------------------------------------


[39/50] [abbrv] cassandra git commit: Fix queries with empty ByteBuffer values in clustering column restrictions

Posted by ms...@apache.org.
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.8
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;
     }
 }


[33/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/903ac4d4
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/903ac4d4
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/903ac4d4

Branch: refs/heads/cassandra-3.8
Commit: 903ac4d4f0944c17e6ec923ffd4cdcaee65eee8d
Parents: d597910 74ac3ce
Author: Jason Brown <ja...@gmail.com>
Authored: Sun Aug 14 08:45:15 2016 -0700
Committer: Jason Brown <ja...@gmail.com>
Committed: Sun Aug 14 08:45:25 2016 -0700

----------------------------------------------------------------------
 .../unit/org/apache/cassandra/streaming/StreamingTransferTest.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/903ac4d4/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
----------------------------------------------------------------------


[35/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2' into cassandra-3.0

Posted by ms...@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/e6dd1524
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e6dd1524
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e6dd1524

Branch: refs/heads/cassandra-3.8
Commit: e6dd15245b87480cfda6b34f9834f341d161ee34
Parents: 74ac3ce 88b3cfc
Author: Yuki Morishita <yu...@apache.org>
Authored: Mon Aug 15 10:09:46 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Mon Aug 15 10:09:46 2016 -0500

----------------------------------------------------------------------
 .../apache/cassandra/streaming/messages/OutgoingFileMessage.java  | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e6dd1524/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
----------------------------------------------------------------------


[11/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/1fff069a
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/1fff069a
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/1fff069a

Branch: refs/heads/cassandra-3.8
Commit: 1fff069ad90b33dcda6d231ca5e327758f844388
Parents: c3e636c 2691c9e
Author: T Jake Luciani <ja...@apache.org>
Authored: Wed Aug 10 14:43:13 2016 -0400
Committer: T Jake Luciani <ja...@apache.org>
Committed: Wed Aug 10 14:43:13 2016 -0400

----------------------------------------------------------------------
 CHANGES.txt                                     |   3 +
 .../metrics/CassandraMetricsRegistry.java       |   4 +-
 .../cassandra/metrics/ClearableHistogram.java   |   4 +-
 .../DecayingEstimatedHistogramReservoir.java    | 549 +++++++++++++++++++
 .../metrics/EstimatedHistogramReservoir.java    | 111 ----
 .../cassandra/utils/EstimatedHistogram.java     |   2 +-
 ...DecayingEstimatedHistogramReservoirTest.java | 381 +++++++++++++
 7 files changed, 938 insertions(+), 116 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1fff069a/CHANGES.txt
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1fff069a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
----------------------------------------------------------------------


[06/50] [abbrv] cassandra git commit: Always use RANGE_SLICE verb for 3.x messages

Posted by ms...@apache.org.
Always use RANGE_SLICE verb for 3.x messages

Patch by Tyler Hobbs; reviewed by Benjamin Lerer for CASSANDRA-12249


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/465def8e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/465def8e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/465def8e

Branch: refs/heads/cassandra-3.8
Commit: 465def8e295b453dc22430dc4bb7039f6921151e
Parents: 62ef861
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Wed Aug 10 11:09:38 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Wed Aug 10 11:09:38 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                             |  2 ++
 src/java/org/apache/cassandra/net/MessageOut.java       |  2 +-
 src/java/org/apache/cassandra/net/MessagingService.java | 10 ++++++++++
 3 files changed, 13 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/465def8e/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index f613c5f..86746ad 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,6 @@
 3.0.9
+ * Fix potential bad messaging service message for paged range reads
+   within mixed-version 3.x clusters (CASSANDRA-12249)
  * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
  * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
  * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/465def8e/src/java/org/apache/cassandra/net/MessageOut.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/net/MessageOut.java b/src/java/org/apache/cassandra/net/MessageOut.java
index a524e7a..ce190cb 100644
--- a/src/java/org/apache/cassandra/net/MessageOut.java
+++ b/src/java/org/apache/cassandra/net/MessageOut.java
@@ -109,7 +109,7 @@ public class MessageOut<T>
     {
         CompactEndpointSerializationHelper.serialize(from, out);
 
-        out.writeInt(verb.ordinal());
+        out.writeInt(MessagingService.Verb.convertForMessagingServiceVersion(verb, version).ordinal());
         out.writeInt(parameters.size());
         for (Map.Entry<String, byte[]> entry : parameters.entrySet())
         {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/465def8e/src/java/org/apache/cassandra/net/MessagingService.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/net/MessagingService.java b/src/java/org/apache/cassandra/net/MessagingService.java
index d01419f..45cfb8a 100644
--- a/src/java/org/apache/cassandra/net/MessagingService.java
+++ b/src/java/org/apache/cassandra/net/MessagingService.java
@@ -148,6 +148,16 @@ public final class MessagingService implements MessagingServiceMBean
         UNUSED_4,
         UNUSED_5,
         ;
+
+        // This is to support a "late" choice of the verb based on the messaging service version.
+        // See CASSANDRA-12249 for more details.
+        public static Verb convertForMessagingServiceVersion(Verb verb, int version)
+        {
+            if (verb == PAGED_RANGE && version >= VERSION_30)
+                return RANGE_SLICE;
+
+            return verb;
+        }
     }
 
     public static final EnumMap<MessagingService.Verb, Stage> verbStages = new EnumMap<MessagingService.Verb, Stage>(MessagingService.Verb.class)


[05/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/8458e4e3
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/8458e4e3
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/8458e4e3

Branch: refs/heads/cassandra-3.8
Commit: 8458e4e30744e8df6fcbe12aec286db047411385
Parents: ee60941 62ef861
Author: Yuki Morishita <yu...@apache.org>
Authored: Tue Aug 9 16:56:29 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Tue Aug 9 16:56:29 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../org/apache/cassandra/config/Config.java     |  4 ++
 .../cassandra/config/DatabaseDescriptor.java    |  5 --
 .../cassandra/streaming/StreamReader.java       | 26 +---------
 .../cassandra/streaming/StreamSession.java      | 36 +-------------
 .../compress/CompressedInputStream.java         | 21 +++++++-
 .../compress/CompressedStreamReader.java        | 10 ++--
 .../streaming/messages/IncomingFileMessage.java | 22 ++-------
 .../streaming/messages/RetryMessage.java        |  4 ++
 .../org/apache/cassandra/utils/Throwables.java  | 14 ++++++
 .../compression/CompressedInputStreamTest.java  | 52 +++++++++++++++++---
 11 files changed, 100 insertions(+), 95 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index b7bbf72,f613c5f..8fbd774
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -22,47 -15,6 +22,48 @@@ Merged from 3.0
     to connect with too low of a protocol version (CASSANDRA-11464)
   * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
   * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
 +Merged from 2.2:
++ * Fix hanging stream session (CASSANDRA-10992)
 + * Fix INSERT JSON, fromJson() support of smallint, tinyint types (CASSANDRA-12371)
 + * Release sstables of failed stream sessions only when outgoing transfers are finished (CASSANDRA-11345)
 + * 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)
 +Merged from 2.1:
 + * cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
 + * Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
 +
 +
 +3.8
 + * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
 + * Fix hdr logging for single operation workloads (CASSANDRA-12145)
 + * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
 + * Increase size of flushExecutor thread pool (CASSANDRA-12071)
 + * Partial revert of CASSANDRA-11971, cannot recycle buffer in SP.sendMessagesToNonlocalDC (CASSANDRA-11950)
 + * Upgrade netty to 4.0.39 (CASSANDRA-12032, CASSANDRA-12034)
 + * Improve details in compaction log message (CASSANDRA-12080)
 + * Allow unset values in CQLSSTableWriter (CASSANDRA-11911)
 + * Chunk cache to request compressor-compatible buffers if pool space is exhausted (CASSANDRA-11993)
 + * Remove DatabaseDescriptor dependencies from SequentialWriter (CASSANDRA-11579)
 + * Move skip_stop_words filter before stemming (CASSANDRA-12078)
 + * Support seek() in EncryptedFileSegmentInputStream (CASSANDRA-11957)
 + * SSTable tools mishandling LocalPartitioner (CASSANDRA-12002)
 + * When SEPWorker assigned work, set thread name to match pool (CASSANDRA-11966)
 + * Add cross-DC latency metrics (CASSANDRA-11596)
 + * Allow terms in selection clause (CASSANDRA-10783)
 + * Add bind variables to trace (CASSANDRA-11719)
 + * Switch counter shards' clock to timestamps (CASSANDRA-9811)
 + * Introduce HdrHistogram and response/service/wait separation to stress tool (CASSANDRA-11853)
 + * entry-weighers in QueryProcessor should respect partitionKeyBindIndexes field (CASSANDRA-11718)
 + * Support older ant versions (CASSANDRA-11807)
 + * Estimate compressed on disk size when deciding if sstable size limit reached (CASSANDRA-11623)
 + * cassandra-stress profiles should support case sensitive schemas (CASSANDRA-11546)
 + * Remove DatabaseDescriptor dependency from FileUtils (CASSANDRA-11578)
 + * Faster streaming (CASSANDRA-9766)
 + * Add prepared query parameter to trace for "Execute CQL3 prepared query" session (CASSANDRA-11425)
 + * Add repaired percentage metric (CASSANDRA-11503)
 + * Add Change-Data-Capture (CASSANDRA-8844)
 +Merged from 3.0:
   * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
   * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
   * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/config/Config.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/StreamReader.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/StreamReader.java
index 0cd6329,4ca7937..816f028
--- a/src/java/org/apache/cassandra/streaming/StreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/StreamReader.java
@@@ -154,32 -153,12 +151,13 @@@ public class StreamReade
      {
          Directories.DataDirectory localDir = cfs.getDirectories().getWriteableLocation(totalSize);
          if (localDir == null)
 -            throw new IOException("Insufficient disk space to store " + totalSize + " bytes");
 -        desc = Descriptor.fromFilename(cfs.getSSTablePath(cfs.getDirectories().getLocationForDisk(localDir), format));
 +            throw new IOException(String.format("Insufficient disk space to store %s", FBUtilities.prettyPrintMemory(totalSize)));
  
 -        return cfs.createSSTableMultiWriter(desc, estimatedKeys, repairedAt, sstableLevel, getHeader(cfs.metadata), session.getTransaction(cfId));
 +        RangeAwareSSTableWriter writer = new RangeAwareSSTableWriter(cfs, estimatedKeys, repairedAt, format, sstableLevel, totalSize, session.getTransaction(cfId), getHeader(cfs.metadata));
 +        StreamHook.instance.reportIncomingFile(cfs, writer, session, fileSeqNum);
 +        return writer;
      }
  
-     protected void drain(InputStream dis, long bytesRead) throws IOException
-     {
-         long toSkip = totalSize() - bytesRead;
- 
-         // InputStream.skip can return -1 if dis is inaccessible.
-         long skipped = dis.skip(toSkip);
-         if (skipped == -1)
-             return;
- 
-         toSkip = toSkip - skipped;
-         while (toSkip > 0)
-         {
-             skipped = dis.skip(toSkip);
-             if (skipped == -1)
-                 break;
-             toSkip = toSkip - skipped;
-         }
-     }
- 
      protected long totalSize()
      {
          long size = 0;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/StreamSession.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
index 3aaa1a3,d59849f..81abefa
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
@@@ -79,31 -90,32 +88,37 @@@ public class CompressedInputStream exte
          // buffer is limited to store up to 1024 chunks
          this.dataBuffer = new ArrayBlockingQueue<>(Math.min(info.chunks.length, 1024));
          this.crcCheckChanceSupplier = crcCheckChanceSupplier;
 +        this.checksumType = checksumType;
  
 -        new Thread(new Reader(source, info, dataBuffer)).start();
 +        new FastThreadLocalThread(new Reader(source, info, dataBuffer)).start();
      }
  
 -    public int read() throws IOException
 +    private void decompressNextChunk() throws IOException
      {
+         if (readException != null)
+             throw readException;
+ 
 -        if (current >= bufferOffset + buffer.length || validBufferBytes == -1)
 +        try
          {
 -            try
 +            byte[] compressedWithCRC = dataBuffer.take();
 +            if (compressedWithCRC == POISON_PILL)
-                 throw new EOFException("No chunk available");
+             {
 -                byte[] compressedWithCRC = dataBuffer.take();
 -                if (compressedWithCRC == POISON_PILL)
 -                {
 -                    assert readException != null;
 -                    throw readException;
 -                }
 -                decompress(compressedWithCRC);
 -            }
 -            catch (InterruptedException e)
 -            {
 -                throw new EOFException("No chunk available");
++                assert readException != null;
++                throw readException;
+             }
 +            decompress(compressedWithCRC);
 +        }
 +        catch (InterruptedException e)
 +        {
 +            throw new EOFException("No chunk available");
          }
 +    }
 +
 +    @Override
 +    public int read() throws IOException
 +    {
 +        if (current >= bufferOffset + buffer.length || validBufferBytes == -1)
 +            decompressNextChunk();
  
          assert current >= bufferOffset && current < bufferOffset + validBufferBytes;
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
index 8dafa9c,bc87c8f..70b5765
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
@@@ -35,9 -36,11 +35,11 @@@ import org.apache.cassandra.streaming.P
  import org.apache.cassandra.streaming.StreamReader;
  import org.apache.cassandra.streaming.StreamSession;
  import org.apache.cassandra.streaming.messages.FileMessageHeader;
 -import org.apache.cassandra.io.util.TrackedInputStream;
 +import org.apache.cassandra.utils.FBUtilities;
  import org.apache.cassandra.utils.Pair;
  
+ import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
+ 
  /**
   * StreamReader that reads from streamed compressed SSTable
   */

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8458e4e3/test/unit/org/apache/cassandra/streaming/compression/CompressedInputStreamTest.java
----------------------------------------------------------------------


[49/50] [abbrv] cassandra git commit: Merge branch cassandra-3.0 into cassandra-3.9

Posted by ms...@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/cassandra-3.8
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")));
+             });
+         }
+     }
  }


[30/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/d597910b
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d597910b
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d597910b

Branch: refs/heads/cassandra-3.8
Commit: d597910b2e5438962eefb5217887e844e011063a
Parents: d052c90 5fdb90e
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Fri Aug 12 18:34:44 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Fri Aug 12 18:34:44 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 NEWS.txt                                        |  2 ++
 .../cassandra/config/DatabaseDescriptor.java    |  6 ++--
 .../cassandra/locator/Ec2MultiRegionSnitch.java |  6 +++-
 .../cassandra/service/StorageService.java       |  4 +--
 .../org/apache/cassandra/transport/Server.java  |  2 +-
 .../org/apache/cassandra/utils/FBUtilities.java | 19 ++++++++++++
 .../apache/cassandra/utils/FBUtilitiesTest.java | 32 ++++++++++++++++++++
 8 files changed, 66 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/CHANGES.txt
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index d8d84f5,6b8dfd3..249077d
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -45,12 -26,11 +45,14 @@@ New feature
  
  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.
++    - Ec2MultiRegionSnitch will no longer automatically set broadcast_rpc_address
++      to the public instance IP if this property is defined on cassandra.yaml.
 +    - The name "json" and "distinct" are not valid anymore a user-defined function
 +      names (they are still valid as column name however). In the unlikely case where
 +      you had defined functions with such names, you will need to recreate
 +      those under a different name, change your code to use the new names and
 +      drop the old versions, and this _before_ upgrade (see CASSANDRA-10783 for more
 +      details).
  
  Deprecation
  -----------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/src/java/org/apache/cassandra/service/StorageService.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/service/StorageService.java
index 7500593,0c91784..bc69d93
--- a/src/java/org/apache/cassandra/service/StorageService.java
+++ b/src/java/org/apache/cassandra/service/StorageService.java
@@@ -785,14 -740,9 +785,14 @@@ public class StorageService extends Not
              getTokenMetadata().updateHostId(localHostId, FBUtilities.getBroadcastAddress());
              appStates.put(ApplicationState.NET_VERSION, valueFactory.networkVersion());
              appStates.put(ApplicationState.HOST_ID, valueFactory.hostId(localHostId));
-             appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(DatabaseDescriptor.getBroadcastRpcAddress()));
+             appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(FBUtilities.getBroadcastRpcAddress()));
              appStates.put(ApplicationState.RELEASE_VERSION, valueFactory.releaseVersion());
 +
 +            // load the persisted ring state. This used to be done earlier in the init process,
 +            // but now we always perform a shadow round when preparing to join and we have to
 +            // clear endpoint states after doing that.
 +            loadRingState();
 +
              logger.info("Starting up server gossip");
              Gossiper.instance.register(this);
              Gossiper.instance.start(SystemKeyspace.incrementAndGetGeneration(), appStates); // needed for node-ring gathering.

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/src/java/org/apache/cassandra/transport/Server.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/src/java/org/apache/cassandra/utils/FBUtilities.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/utils/FBUtilities.java
index af2cb1b,ca2775f..c76d8e1
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@@ -892,21 -872,12 +903,29 @@@ public class FBUtilitie
              throw new RuntimeException(e);
          }
      }
 +	
 +	public static void sleepQuietly(long millis)
 +    {
 +        try
 +        {
 +            Thread.sleep(millis);
 +        }
 +        catch (InterruptedException e)
 +        {
 +            throw new RuntimeException(e);
 +        }
 +    }
 +
 +    public static long align(long val, int boundary)
 +    {
 +        return (val + boundary) & ~(boundary - 1);
 +    }
+ 
+     @VisibleForTesting
+     protected static void reset()
+     {
+         localInetAddress = null;
+         broadcastInetAddress = null;
+         broadcastRpcAddress = null;
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d597910b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
index 3c1ea74,acd68eb..b8d2633
--- a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
+++ b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
@@@ -22,18 -23,16 +23,21 @@@ import java.net.InetAddress
  import java.nio.ByteBuffer;
  import java.nio.charset.CharacterCodingException;
  import java.nio.charset.StandardCharsets;
 +import java.util.Map;
 +import java.util.Optional;
 +import java.util.TreeMap;
  
  import com.google.common.primitives.Ints;
 +
 +import org.junit.Assert;
  import org.junit.Test;
  
 -import java.util.Map;
 -import java.util.TreeMap;
 +import org.apache.cassandra.db.marshal.*;
 +import org.apache.cassandra.dht.*;
  
+ import org.apache.cassandra.config.Config;
+ import org.apache.cassandra.config.DatabaseDescriptor;
+ 
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.fail;
  
@@@ -101,50 -100,31 +105,78 @@@ public class FBUtilitiesTes
          ByteBufferUtil.string(bytes, StandardCharsets.UTF_8);
      }
  
 +    private static void assertPartitioner(String name, Class expected)
 +    {
 +        Assert.assertTrue(String.format("%s != %s", name, expected.toString()),
 +                          expected.isInstance(FBUtilities.newPartitioner(name)));
 +    }
 +
 +    /**
 +     * Check that given a name, the correct partitioner instance is created.
 +     *
 +     * If the assertions in this test start failing, it likely means the sstabledump/sstablemetadata tools will
 +     * also fail to read existing sstables.
 +     */
 +    @Test
 +    public void testNewPartitionerNoArgConstructors()
 +    {
 +        assertPartitioner("ByteOrderedPartitioner", ByteOrderedPartitioner.class);
 +        assertPartitioner("LengthPartitioner", LengthPartitioner.class);
 +        assertPartitioner("Murmur3Partitioner", Murmur3Partitioner.class);
 +        assertPartitioner("OrderPreservingPartitioner", OrderPreservingPartitioner.class);
 +        assertPartitioner("RandomPartitioner", RandomPartitioner.class);
 +        assertPartitioner("org.apache.cassandra.dht.ByteOrderedPartitioner", ByteOrderedPartitioner.class);
 +        assertPartitioner("org.apache.cassandra.dht.LengthPartitioner", LengthPartitioner.class);
 +        assertPartitioner("org.apache.cassandra.dht.Murmur3Partitioner", Murmur3Partitioner.class);
 +        assertPartitioner("org.apache.cassandra.dht.OrderPreservingPartitioner", OrderPreservingPartitioner.class);
 +        assertPartitioner("org.apache.cassandra.dht.RandomPartitioner", RandomPartitioner.class);
 +    }
 +
 +    /**
 +     * Check that we can instantiate local partitioner correctly and that we can pass the correct type
 +     * to it as a constructor argument.
 +     *
 +     * If the assertions in this test start failing, it likely means the sstabledump/sstablemetadata tools will
 +     * also fail to read existing sstables.
 +     */
 +    @Test
 +    public void testNewPartitionerLocalPartitioner()
 +    {
 +        for (String name : new String[] {"LocalPartitioner", "org.apache.cassandra.dht.LocalPartitioner"})
 +            for (AbstractType<?> type : new AbstractType<?>[] {UUIDType.instance, ListType.getInstance(Int32Type.instance, true)})
 +            {
 +                IPartitioner partitioner = FBUtilities.newPartitioner(name, Optional.of(type));
 +                Assert.assertTrue(String.format("%s != LocalPartitioner", partitioner.toString()),
 +                                  LocalPartitioner.class.isInstance(partitioner));
 +                Assert.assertEquals(partitioner.partitionOrdering(), type);
 +            }
 +    }
++
+     @Test
+     public void testGetBroadcastRpcAddress() throws Exception
+     {
+         //When both rpc_address and broadcast_rpc_address are null, it should return the local address (from DD.applyAddressConfig)
+         FBUtilities.reset();
+         Config testConfig = DatabaseDescriptor.loadConfig();
+         testConfig.rpc_address = null;
+         testConfig.broadcast_rpc_address = null;
+         DatabaseDescriptor.applyAddressConfig(testConfig);
+         assertEquals(FBUtilities.getLocalAddress(), FBUtilities.getBroadcastRpcAddress());
+ 
+         //When rpc_address is defined and broadcast_rpc_address is null, it should return the rpc_address
+         FBUtilities.reset();
+         testConfig.rpc_address = "127.0.0.2";
+         testConfig.broadcast_rpc_address = null;
+         DatabaseDescriptor.applyAddressConfig(testConfig);
+         assertEquals(InetAddress.getByName("127.0.0.2"), FBUtilities.getBroadcastRpcAddress());
+ 
+         //When both rpc_address and broadcast_rpc_address are defined, it should return broadcast_rpc_address
+         FBUtilities.reset();
+         testConfig.rpc_address = "127.0.0.2";
+         testConfig.broadcast_rpc_address = "127.0.0.3";
+         DatabaseDescriptor.applyAddressConfig(testConfig);
+         assertEquals(InetAddress.getByName("127.0.0.3"), FBUtilities.getBroadcastRpcAddress());
+ 
+         FBUtilities.reset();
+     }
  }


[44/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into cassandra-2.2

Posted by ms...@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()


[36/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/e72dc29e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e72dc29e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e72dc29e

Branch: refs/heads/cassandra-3.8
Commit: e72dc29e69ed21d15c70788cd2c2b620eeabeb6c
Parents: 903ac4d e6dd152
Author: Yuki Morishita <yu...@apache.org>
Authored: Mon Aug 15 10:09:55 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Mon Aug 15 10:09:55 2016 -0500

----------------------------------------------------------------------
 .../apache/cassandra/streaming/messages/OutgoingFileMessage.java  | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e72dc29e/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
----------------------------------------------------------------------


[17/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/02f48c86
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/02f48c86
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/02f48c86

Branch: refs/heads/cassandra-3.8
Commit: 02f48c86591939e3cd20157af867dfec94ca443b
Parents: 6a846e6 19245e5
Author: Yuki Morishita <yu...@apache.org>
Authored: Thu Aug 11 11:06:20 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Thu Aug 11 11:06:20 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                            |  1 +
 .../cassandra/utils/RMIServerSocketFactoryImpl.java    | 13 +++++++++++--
 2 files changed, 12 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/02f48c86/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index bca8879,0a1d406..29a6c50
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,12 -1,9 +1,13 @@@
 -3.0.9
 +3.9
 + * Fix value skipping with counter columns (CASSANDRA-11726)
 + * Fix nodetool tablestats miss SSTable count (CASSANDRA-12205)
 + * Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
 + * Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)
 + * cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
 + * Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
 +Merged from 3.0:
+  * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
 - * Backport CASSANDRA-12002 (CASSANDRA-12177)
   * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
 - * Fix potential bad messaging service message for paged range reads
 -   within mixed-version 3.x clusters (CASSANDRA-12249)
   * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
   * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
   * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/02f48c86/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
index 4ac4a39,6444a65..9baf0c7
--- a/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
+++ b/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
@@@ -38,9 -33,18 +39,17 @@@ public class RMIServerSocketFactoryImp
  
      public ServerSocket createServerSocket(final int pPort) throws IOException
      {
 -        ServerSocket socket = ServerSocketFactory.getDefault()
 -                                                 .createServerSocket(pPort, 0, InetAddress.getLoopbackAddress());
 +        ServerSocket socket = ServerSocketFactory.getDefault().createServerSocket(pPort, 0, bindAddress);
-         socket.setReuseAddress(true);
-         return socket;
+         try
+         {
+             socket.setReuseAddress(true);
+             return socket;
+         }
+         catch (SocketException e)
+         {
+             socket.close();
+             throw e;
+         }
      }
  
      public boolean equals(Object obj)


[15/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/6a846e6f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/6a846e6f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/6a846e6f

Branch: refs/heads/cassandra-3.8
Commit: 6a846e6f8443280f41a121f318ca192c8d0d97ea
Parents: 15fd71f de86ab1
Author: Yuki Morishita <yu...@apache.org>
Authored: Thu Aug 11 10:11:50 2016 -0500
Committer: Yuki Morishita <yu...@apache.org>
Committed: Thu Aug 11 10:11:50 2016 -0500

----------------------------------------------------------------------

----------------------------------------------------------------------



[28/50] [abbrv] cassandra git commit: Only set broadcast_rpc_address on Ec2MultiRegionSnitch if it's not set

Posted by ms...@apache.org.
Only set broadcast_rpc_address on Ec2MultiRegionSnitch if it's not set

Patch by Paulo Motta; reviewed by Tyler Hobbs for CASSANDRA-11356


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/91f7387e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/91f7387e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/91f7387e

Branch: refs/heads/cassandra-3.8
Commit: 91f7387e1f785b18321777311a5c3416af0663c2
Parents: e86d531
Author: Paulo Motta <pa...@gmail.com>
Authored: Tue Aug 2 12:42:20 2016 -0300
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Fri Aug 12 18:25:19 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 NEWS.txt                                        |  6 ++++
 .../cassandra/config/DatabaseDescriptor.java    |  6 ++--
 .../cassandra/locator/Ec2MultiRegionSnitch.java |  6 +++-
 .../cassandra/service/StorageService.java       |  4 +--
 .../org/apache/cassandra/transport/Server.java  |  2 +-
 .../org/apache/cassandra/utils/FBUtilities.java | 20 ++++++++++++
 .../apache/cassandra/utils/FBUtilitiesTest.java | 32 ++++++++++++++++++++
 8 files changed, 71 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index ddc6720..394598a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index a3ba0dd..f0712eb 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -21,6 +21,12 @@ New features
     - JSON timestamps are now in UTC and contain the timezone information, see
       CASSANDRA-11137 for more details.
 
+Upgrading
+---------
+    - Ec2MultiRegionSnitch will no longer automatically set broadcast_rpc_address
+      to the public instance IP if this property is defined on cassandra.yaml.
+
+
 2.2.6
 =====
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 6e46725..75f80b9 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -174,7 +174,7 @@ public class DatabaseDescriptor
     }
 
     @VisibleForTesting
-    static void applyAddressConfig(Config config) throws ConfigurationException
+    public static void applyAddressConfig(Config config) throws ConfigurationException
     {
         listenAddress = null;
         rpcAddress = null;
@@ -266,7 +266,6 @@ public class DatabaseDescriptor
             if (rpcAddress.isAnyLocalAddress())
                 throw new ConfigurationException("If rpc_address is set to a wildcard address (" + config.rpc_address + "), then " +
                                                  "you must set broadcast_rpc_address to a value other than " + config.rpc_address, false);
-            broadcastRpcAddress = rpcAddress;
         }
     }
 
@@ -1269,6 +1268,9 @@ public class DatabaseDescriptor
         broadcastRpcAddress = broadcastRPCAddr;
     }
 
+    /**
+     * May be null, please use {@link FBUtilities#getBroadcastRpcAddress()} instead.
+     */
     public static InetAddress getBroadcastRpcAddress()
     {
         return broadcastRpcAddress;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java b/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java
index ab1b5d0..b32ca84 100644
--- a/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java
+++ b/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java
@@ -51,7 +51,11 @@ public class Ec2MultiRegionSnitch extends Ec2Snitch
         localPrivateAddress = awsApiCall(PRIVATE_IP_QUERY_URL);
         // use the Public IP to broadcast Address to other nodes.
         DatabaseDescriptor.setBroadcastAddress(localPublicAddress);
-        DatabaseDescriptor.setBroadcastRpcAddress(localPublicAddress);
+        if (DatabaseDescriptor.getBroadcastRpcAddress() == null)
+        {
+            logger.info("broadcast_rpc_address unset, broadcasting public IP as rpc_address: {}", localPublicAddress);
+            DatabaseDescriptor.setBroadcastRpcAddress(localPublicAddress);
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/service/StorageService.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java
index fa04595..3148f5e 100644
--- a/src/java/org/apache/cassandra/service/StorageService.java
+++ b/src/java/org/apache/cassandra/service/StorageService.java
@@ -697,7 +697,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
             getTokenMetadata().updateHostId(localHostId, FBUtilities.getBroadcastAddress());
             appStates.put(ApplicationState.NET_VERSION, valueFactory.networkVersion());
             appStates.put(ApplicationState.HOST_ID, valueFactory.hostId(localHostId));
-            appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(DatabaseDescriptor.getBroadcastRpcAddress()));
+            appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(FBUtilities.getBroadcastRpcAddress()));
             appStates.put(ApplicationState.RELEASE_VERSION, valueFactory.releaseVersion());
             logger.info("Starting up server gossip");
             Gossiper.instance.register(this);
@@ -1282,7 +1282,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
     public String getRpcaddress(InetAddress endpoint)
     {
         if (endpoint.equals(FBUtilities.getBroadcastAddress()))
-            return DatabaseDescriptor.getBroadcastRpcAddress().getHostAddress();
+            return FBUtilities.getBroadcastRpcAddress().getHostAddress();
         else if (Gossiper.instance.getEndpointStateForEndpoint(endpoint).getApplicationState(ApplicationState.RPC_ADDRESS) == null)
             return endpoint.getHostAddress();
         else

http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/transport/Server.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/transport/Server.java b/src/java/org/apache/cassandra/transport/Server.java
index 5c0d9d2..d1047f9 100644
--- a/src/java/org/apache/cassandra/transport/Server.java
+++ b/src/java/org/apache/cassandra/transport/Server.java
@@ -486,7 +486,7 @@ public class Server implements CassandraDaemon.Server
             // which is not useful to any driver and in fact may cauase serious problems to some drivers,
             // see CASSANDRA-10052
             if (!endpoint.equals(FBUtilities.getBroadcastAddress()) &&
-                event.nodeAddress().equals(DatabaseDescriptor.getBroadcastRpcAddress()))
+                event.nodeAddress().equals(FBUtilities.getBroadcastRpcAddress()))
                 return;
 
             send(event);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/src/java/org/apache/cassandra/utils/FBUtilities.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/FBUtilities.java b/src/java/org/apache/cassandra/utils/FBUtilities.java
index 9eda878..23a2c2e 100644
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@ -34,6 +34,7 @@ import java.util.zip.Checksum;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.collect.AbstractIterator;
 import org.apache.commons.lang3.StringUtils;
@@ -74,6 +75,7 @@ public class FBUtilities
 
     private static volatile InetAddress localInetAddress;
     private static volatile InetAddress broadcastInetAddress;
+    private static volatile InetAddress broadcastRpcAddress;
 
     public static int getAvailableProcessors()
     {
@@ -147,6 +149,16 @@ public class FBUtilities
         return broadcastInetAddress;
     }
 
+
+    public static InetAddress getBroadcastRpcAddress()
+    {
+        if (broadcastRpcAddress == null)
+            broadcastRpcAddress = DatabaseDescriptor.getBroadcastRpcAddress() == null
+                                   ? DatabaseDescriptor.getRpcAddress()
+                                   : DatabaseDescriptor.getBroadcastRpcAddress();
+        return broadcastRpcAddress;
+    }
+
     public static Collection<InetAddress> getAllLocalAddresses()
     {
         Set<InetAddress> localAddresses = new HashSet<InetAddress>();
@@ -824,4 +836,12 @@ public class FBUtilities
             throw new RuntimeException(e);
         }
     }
+
+    @VisibleForTesting
+    protected static void reset()
+    {
+        localInetAddress = null;
+        broadcastInetAddress = null;
+        broadcastRpcAddress = null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/91f7387e/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
index c82bcc9..5b86252 100644
--- a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
+++ b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
@@ -21,6 +21,7 @@ package org.apache.cassandra.utils;
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
+import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.StandardCharsets;
@@ -30,6 +31,9 @@ import org.junit.Test;
 import java.util.Map;
 import java.util.TreeMap;
 
+import org.apache.cassandra.config.Config;
+import org.apache.cassandra.config.DatabaseDescriptor;
+
 import static org.junit.Assert.assertEquals;
 
 public class FBUtilitiesTest
@@ -95,4 +99,32 @@ public class FBUtilitiesTest
         ByteBuffer bytes = ByteBuffer.wrap(new byte[]{(byte)0xff, (byte)0xfe});
         ByteBufferUtil.string(bytes, StandardCharsets.UTF_8);
     }
+
+    @Test
+    public void testGetBroadcastRpcAddress() throws Exception
+    {
+        //When both rpc_address and broadcast_rpc_address are null, it should return the local address (from DD.applyAddressConfig)
+        FBUtilities.reset();
+        Config testConfig = DatabaseDescriptor.loadConfig();
+        testConfig.rpc_address = null;
+        testConfig.broadcast_rpc_address = null;
+        DatabaseDescriptor.applyAddressConfig(testConfig);
+        assertEquals(FBUtilities.getLocalAddress(), FBUtilities.getBroadcastRpcAddress());
+
+        //When rpc_address is defined and broadcast_rpc_address is null, it should return the rpc_address
+        FBUtilities.reset();
+        testConfig.rpc_address = "127.0.0.2";
+        testConfig.broadcast_rpc_address = null;
+        DatabaseDescriptor.applyAddressConfig(testConfig);
+        assertEquals(InetAddress.getByName("127.0.0.2"), FBUtilities.getBroadcastRpcAddress());
+
+        //When both rpc_address and broadcast_rpc_address are defined, it should return broadcast_rpc_address
+        FBUtilities.reset();
+        testConfig.rpc_address = "127.0.0.2";
+        testConfig.broadcast_rpc_address = "127.0.0.3";
+        DatabaseDescriptor.applyAddressConfig(testConfig);
+        assertEquals(InetAddress.getByName("127.0.0.3"), FBUtilities.getBroadcastRpcAddress());
+
+        FBUtilities.reset();
+    }
 }


[45/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into cassandra-2.2

Posted by ms...@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/cassandra-3.8
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;
 +    }
 +}


[08/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.8' into cassandra-3.9

Posted by ms...@apache.org.
Merge branch 'cassandra-3.8' 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/c3e636c5
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c3e636c5
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c3e636c5

Branch: refs/heads/cassandra-3.8
Commit: c3e636c528a09371255d460ad6a91eb0c1d47875
Parents: 2d769f0 b188e08
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Wed Aug 10 11:12:33 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Wed Aug 10 11:12:33 2016 -0500

----------------------------------------------------------------------

----------------------------------------------------------------------



[47/50] [abbrv] cassandra git commit: Merge branch cassandra-2.2 into cassandra-3.0

Posted by ms...@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)
 +        );
 +    }
  }


[43/50] [abbrv] cassandra git commit: Merge branch cassandra-2.1 into cassandra-2.2

Posted by ms...@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);
      }
  


[20/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/f4c3e14f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/f4c3e14f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/f4c3e14f

Branch: refs/heads/cassandra-3.8
Commit: f4c3e14fdebe9ec3fb9be4a6918b55f489891f3a
Parents: 02f48c8 2a828af
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Thu Aug 11 13:47:11 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Thu Aug 11 13:47:11 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt | 1 +
 build.xml   | 4 ++++
 2 files changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4c3e14f/CHANGES.txt
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4c3e14f/build.xml
----------------------------------------------------------------------


[03/50] [abbrv] cassandra git commit: Fix hanging stream session

Posted by ms...@apache.org.
Fix hanging stream session

by preventing CompressedStreamReader from blocking on IOException.
Also removed retry support from streaming.

Patch by Paulo Motta; Reviewed by Yuki Morishita for CASSANDRA-10992


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/76e3100f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/76e3100f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/76e3100f

Branch: refs/heads/cassandra-3.8
Commit: 76e3100ffb106cab3cc665404e293c1026e5e65c
Parents: bc9af92
Author: Paulo Motta <pa...@gmail.com>
Authored: Thu Jun 23 11:33:54 2016 -0300
Committer: Yuki Morishita <yu...@apache.org>
Committed: Tue Aug 9 16:31:34 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../org/apache/cassandra/config/Config.java     |  4 ++
 .../cassandra/config/DatabaseDescriptor.java    |  5 --
 .../cassandra/streaming/StreamReader.java       | 26 +----------
 .../cassandra/streaming/StreamSession.java      | 36 +-------------
 .../compress/CompressedInputStream.java         | 21 ++++++++-
 .../compress/CompressedStreamReader.java        | 10 ++--
 .../streaming/messages/IncomingFileMessage.java | 22 ++-------
 .../streaming/messages/RetryMessage.java        |  4 ++
 .../org/apache/cassandra/utils/Throwables.java  | 17 +++++++
 .../compress/CompressedInputStreamTest.java     | 49 +++++++++++++++++---
 11 files changed, 102 insertions(+), 93 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index f734476..232203e 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.8
+ * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/config/Config.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java
index ede4560..60daee6 100644
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@ -171,6 +171,10 @@ public class Config
     public volatile Integer compaction_throughput_mb_per_sec = 16;
     public volatile Integer compaction_large_partition_warning_threshold_mb = 100;
 
+    /**
+     * @deprecated retry support removed on CASSANDRA-10992
+     */
+    @Deprecated
     public Integer max_streaming_retries = 3;
 
     public volatile Integer stream_throughput_outbound_megabits_per_sec = 200;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index f1acfc4..6e46725 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -957,11 +957,6 @@ public class DatabaseDescriptor
         return conf.cluster_name;
     }
 
-    public static int getMaxStreamingRetries()
-    {
-        return conf.max_streaming_retries;
-    }
-
     public static int getStoragePort()
     {
         return Integer.parseInt(System.getProperty("cassandra.storage_port", conf.storage_port.toString()));

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/StreamReader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/StreamReader.java b/src/java/org/apache/cassandra/streaming/StreamReader.java
index 8789720..c96ea22 100644
--- a/src/java/org/apache/cassandra/streaming/StreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/StreamReader.java
@@ -45,6 +45,7 @@ import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.BytesReadTracker;
 import org.apache.cassandra.utils.Pair;
 
+import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
 
 /**
  * StreamReader reads from stream and writes to SSTable.
@@ -137,11 +138,7 @@ public class StreamReader
                     e.addSuppressed(e2);
                 }
             }
-            drain(dis, in.getBytesRead());
-            if (e instanceof IOException)
-                throw (IOException) e;
-            else
-                throw Throwables.propagate(e);
+            throw Throwables.propagate(e);
         }
     }
 
@@ -155,25 +152,6 @@ public class StreamReader
         return SSTableWriter.create(desc, estimatedKeys, repairedAt, sstableLevel);
     }
 
-    protected void drain(InputStream dis, long bytesRead) throws IOException
-    {
-        long toSkip = totalSize() - bytesRead;
-
-        // InputStream.skip can return -1 if dis is inaccessible.
-        long skipped = dis.skip(toSkip);
-        if (skipped == -1)
-            return;
-
-        toSkip = toSkip - skipped;
-        while (toSkip > 0)
-        {
-            skipped = dis.skip(toSkip);
-            if (skipped == -1)
-                break;
-            toSkip = toSkip - skipped;
-        }
-    }
-
     protected long totalSize()
     {
         long size = 0;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/StreamSession.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/StreamSession.java b/src/java/org/apache/cassandra/streaming/StreamSession.java
index 294b9c1..0f43f1f 100644
--- a/src/java/org/apache/cassandra/streaming/StreamSession.java
+++ b/src/java/org/apache/cassandra/streaming/StreamSession.java
@@ -105,10 +105,8 @@ import org.apache.cassandra.utils.concurrent.Refs;
  *       complete (received()). When all files for the StreamReceiveTask have been received, the sstables
  *       are added to the CFS (and 2ndary index are built, StreamReceiveTask.complete()) and the task
  *       is marked complete (taskCompleted())
- *   (b) If during the streaming of a particular file an I/O error occurs on the receiving end of a stream
- *       (FileMessage.deserialize), the node will retry the file (up to DatabaseDescriptor.getMaxStreamingRetries())
- *       by sending a RetryMessage to the sender. On receiving a RetryMessage, the sender simply issue a new
- *       FileMessage for that file.
+ *   (b) If during the streaming of a particular file an error occurs on the receiving end of a stream
+ *       (FileMessage.deserialize), the node will send a SessionFailedMessage to the sender and close the stream session.
  *   (c) When all transfer and receive tasks for a session are complete, the move to the Completion phase
  *       (maybeCompleted()).
  *
@@ -149,8 +147,6 @@ public class StreamSession implements IEndpointStateChangeSubscriber
 
     public final ConnectionHandler handler;
 
-    private int retries;
-
     private AtomicBoolean isAborted = new AtomicBoolean(false);
     private final boolean keepSSTableLevel;
     private final boolean isIncremental;
@@ -481,11 +477,6 @@ public class StreamSession implements IEndpointStateChangeSubscriber
                 received(received.cfId, received.sequenceNumber);
                 break;
 
-            case RETRY:
-                RetryMessage retry = (RetryMessage) message;
-                retry(retry.cfId, retry.sequenceNumber);
-                break;
-
             case COMPLETE:
                 complete();
                 break;
@@ -610,18 +601,6 @@ public class StreamSession implements IEndpointStateChangeSubscriber
     }
 
     /**
-     * Call back on receiving {@code StreamMessage.Type.RETRY} message.
-     *
-     * @param cfId ColumnFamily ID
-     * @param sequenceNumber Sequence number to indicate which file to stream again
-     */
-    public void retry(UUID cfId, int sequenceNumber)
-    {
-        OutgoingFileMessage message = transfers.get(cfId).createMessageForRetry(sequenceNumber);
-        handler.sendMessage(message);
-    }
-
-    /**
      * Check if session is completed on receiving {@code StreamMessage.Type.COMPLETE} message.
      */
     public synchronized void complete()
@@ -651,17 +630,6 @@ public class StreamSession implements IEndpointStateChangeSubscriber
         closeSession(State.FAILED);
     }
 
-    public void doRetry(FileMessageHeader header, Throwable e)
-    {
-        logger.warn("[Stream #{}] Retrying for following error", planId(), e);
-        // retry
-        retries++;
-        if (retries > DatabaseDescriptor.getMaxStreamingRetries())
-            onError(new IOException("Too many retries for " + header, e));
-        else
-            handler.sendMessage(new RetryMessage(header.cfId, header.sequenceNumber));
-    }
-
     /**
      * @return Current snapshot of this session info.
      */

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java b/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
index 489fed9..d08ffa9 100644
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedInputStream.java
@@ -63,8 +63,17 @@ public class CompressedInputStream extends InputStream
     // raw checksum bytes
     private final byte[] checksumBytes = new byte[4];
 
+    /**
+     * Indicates there was a problem when reading from source stream.
+     * When this is added to the <code>dataBuffer</code> by the stream Reader,
+     * it is expected that the <code>readException</code> variable is populated
+     * with the cause of the error when reading from source stream, so it is
+     * thrown to the consumer on subsequent read operation.
+     */
     private static final byte[] POISON_PILL = new byte[0];
 
+    protected volatile IOException readException = null;
+
     private long totalCompressedBytesRead;
 
     /**
@@ -84,13 +93,19 @@ public class CompressedInputStream extends InputStream
 
     public int read() throws IOException
     {
+        if (readException != null)
+            throw readException;
+
         if (current >= bufferOffset + buffer.length || validBufferBytes == -1)
         {
             try
             {
                 byte[] compressedWithCRC = dataBuffer.take();
                 if (compressedWithCRC == POISON_PILL)
-                    throw new EOFException("No chunk available");
+                {
+                    assert readException != null;
+                    throw readException;
+                }
                 decompress(compressedWithCRC);
             }
             catch (InterruptedException e)
@@ -138,7 +153,7 @@ public class CompressedInputStream extends InputStream
         return totalCompressedBytesRead;
     }
 
-    static class Reader extends WrappedRunnable
+    class Reader extends WrappedRunnable
     {
         private final InputStream source;
         private final Iterator<CompressionMetadata.Chunk> chunks;
@@ -169,6 +184,7 @@ public class CompressedInputStream extends InputStream
                         int r = source.read(compressedWithCRC, bufferRead, readLength - bufferRead);
                         if (r < 0)
                         {
+                            readException = new EOFException("No chunk available");
                             dataBuffer.put(POISON_PILL);
                             return; // throw exception where we consume dataBuffer
                         }
@@ -177,6 +193,7 @@ public class CompressedInputStream extends InputStream
                     catch (IOException e)
                     {
                         logger.warn("Error while reading compressed input stream.", e);
+                        readException = e;
                         dataBuffer.put(POISON_PILL);
                         return; // throw exception where we consume dataBuffer
                     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java b/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
index c684e4f..fa1022d 100644
--- a/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
+++ b/src/java/org/apache/cassandra/streaming/compress/CompressedStreamReader.java
@@ -44,6 +44,8 @@ import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.BytesReadTracker;
 import org.apache.cassandra.utils.Pair;
 
+import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
+
 /**
  * StreamReader that reads from streamed compressed SSTable
  */
@@ -132,11 +134,9 @@ public class CompressedStreamReader extends StreamReader
                     e.addSuppressed(e2);
                 }
             }
-            drain(cis, in.getBytesRead());
-            if (e instanceof IOException)
-                throw (IOException) e;
-            else
-                throw Throwables.propagate(e);
+            if (extractIOExceptionCause(e).isPresent())
+                throw e;
+            throw Throwables.propagate(e);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java b/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
index 31ab2a8..2870c03 100644
--- a/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
+++ b/src/java/org/apache/cassandra/streaming/messages/IncomingFileMessage.java
@@ -22,6 +22,8 @@ import java.io.IOException;
 import java.nio.channels.Channels;
 import java.nio.channels.ReadableByteChannel;
 
+import com.google.common.base.Optional;
+
 import org.apache.cassandra.io.sstable.format.SSTableWriter;
 import org.apache.cassandra.io.util.DataOutputStreamPlus;
 import org.apache.cassandra.streaming.StreamReader;
@@ -29,6 +31,8 @@ import org.apache.cassandra.streaming.StreamSession;
 import org.apache.cassandra.streaming.compress.CompressedStreamReader;
 import org.apache.cassandra.utils.JVMStabilityInspector;
 
+import static org.apache.cassandra.utils.Throwables.extractIOExceptionCause;
+
 /**
  * IncomingFileMessage is used to receive the part(or whole) of a SSTable data file.
  */
@@ -48,26 +52,10 @@ public class IncomingFileMessage extends StreamMessage
             {
                 return new IncomingFileMessage(reader.read(in), header);
             }
-            catch (IOException eof)
-            {
-                // Reading from remote failed(i.e. reached EOF before reading expected length of data).
-                // This can be caused by network/node failure thus we are not retrying
-                throw eof;
-            }
             catch (Throwable t)
             {
-                // Throwable can be Runtime error containing IOException.
-                // In that case we don't want to retry.
-                Throwable cause = t;
-                while ((cause = cause.getCause()) != null)
-                {
-                   if (cause instanceof IOException)
-                       throw (IOException) cause;
-                }
                 JVMStabilityInspector.inspectThrowable(t);
-                // Otherwise, we can retry
-                session.doRetry(header, t);
-                return null;
+                throw t;
             }
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java b/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
index 29e84bf..6673aa1 100644
--- a/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
+++ b/src/java/org/apache/cassandra/streaming/messages/RetryMessage.java
@@ -27,6 +27,10 @@ import org.apache.cassandra.net.MessagingService;
 import org.apache.cassandra.streaming.StreamSession;
 import org.apache.cassandra.utils.UUIDSerializer;
 
+/**
+ * @deprecated retry support removed on CASSANDRA-10992
+ */
+@Deprecated
 public class RetryMessage extends StreamMessage
 {
     public static Serializer<RetryMessage> serializer = new Serializer<RetryMessage>()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/src/java/org/apache/cassandra/utils/Throwables.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/Throwables.java b/src/java/org/apache/cassandra/utils/Throwables.java
index a895f31..877f388 100644
--- a/src/java/org/apache/cassandra/utils/Throwables.java
+++ b/src/java/org/apache/cassandra/utils/Throwables.java
@@ -18,6 +18,10 @@
 */
 package org.apache.cassandra.utils;
 
+import java.io.IOException;
+
+import com.google.common.base.Optional;
+
 public class Throwables
 {
 
@@ -50,4 +54,17 @@ public class Throwables
         }
         return accumulate;
     }
+
+    public static Optional<IOException> extractIOExceptionCause(Throwable t)
+    {
+        if (t instanceof IOException)
+            return Optional.of((IOException) t);
+        Throwable cause = t;
+        while ((cause = cause.getCause()) != null)
+        {
+            if (cause instanceof IOException)
+                return Optional.of((IOException) cause);
+        }
+        return Optional.absent();
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/76e3100f/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java b/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java
index 0becd18..87d93fd 100644
--- a/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java
+++ b/test/unit/org/apache/cassandra/streaming/compress/CompressedInputStreamTest.java
@@ -36,6 +36,7 @@ import org.apache.cassandra.io.sstable.metadata.MetadataCollector;
 import org.apache.cassandra.utils.Pair;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 /**
  */
@@ -44,24 +45,33 @@ public class CompressedInputStreamTest
     @Test
     public void testCompressedRead() throws Exception
     {
-        testCompressedReadWith(new long[]{0L}, false);
-        testCompressedReadWith(new long[]{1L}, false);
-        testCompressedReadWith(new long[]{100L}, false);
+        testCompressedReadWith(new long[]{0L}, false, false);
+        testCompressedReadWith(new long[]{1L}, false, false);
+        testCompressedReadWith(new long[]{100L}, false, false);
 
-        testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false);
+        testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false, false);
     }
 
     @Test(expected = EOFException.class)
     public void testTruncatedRead() throws Exception
     {
-        testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, true);
+        testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, true, false);
+    }
+
+    /**
+     * Test that CompressedInputStream does not block if there's an exception while reading stream
+     */
+    @Test(timeout = 30000)
+    public void testException() throws Exception
+    {
+        testCompressedReadWith(new long[]{1L, 122L, 123L, 124L, 456L}, false, true);
     }
 
     /**
      * @param valuesToCheck array of longs of range(0-999)
      * @throws Exception
      */
-    private void testCompressedReadWith(long[] valuesToCheck, boolean testTruncate) throws Exception
+    private void testCompressedReadWith(long[] valuesToCheck, boolean testTruncate, boolean testException) throws Exception
     {
         assert valuesToCheck != null && valuesToCheck.length > 0;
 
@@ -120,6 +130,12 @@ public class CompressedInputStreamTest
 
         // read buffer using CompressedInputStream
         CompressionInfo info = new CompressionInfo(chunks, param);
+
+        if (testException)
+        {
+            testException(sections, info);
+            return;
+        }
         CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(toRead), info);
 
         try (DataInputStream in = new DataInputStream(input))
@@ -132,4 +148,25 @@ public class CompressedInputStreamTest
             }
         }
     }
+
+    private static void testException(List<Pair<Long, Long>> sections, CompressionInfo info) throws IOException
+    {
+        CompressedInputStream input = new CompressedInputStream(new ByteArrayInputStream(new byte[0]), info);
+
+        try (DataInputStream in = new DataInputStream(input))
+        {
+            for (int i = 0; i < sections.size(); i++)
+            {
+                input.position(sections.get(i).left);
+                try {
+                    in.readLong();
+                    fail("Should have thrown IOException");
+                }
+                catch (IOException e)
+                {
+                    continue;
+                }
+            }
+        }
+    }
 }


[09/50] [abbrv] cassandra git commit: Add decay to histograms and timers used for metrics

Posted by ms...@apache.org.
Add decay to histograms and timers used for metrics

Patch by Per Otterstrom; reviewed by tjake for CASSANDRA-11752


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/2e902596
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/2e902596
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/2e902596

Branch: refs/heads/cassandra-3.8
Commit: 2e90259669bddf04b6c1dba38b604aa6a33dcd47
Parents: 76e3100
Author: Per Otterstrom <pe...@ericsson.com>
Authored: Thu Jun 23 01:01:39 2016 +0200
Committer: T Jake Luciani <ja...@apache.org>
Committed: Wed Aug 10 14:37:45 2016 -0400

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../metrics/CassandraMetricsRegistry.java       |   4 +-
 .../cassandra/metrics/ClearableHistogram.java   |   4 +-
 .../DecayingEstimatedHistogramReservoir.java    | 549 +++++++++++++++++++
 .../metrics/EstimatedHistogramReservoir.java    | 111 ----
 .../cassandra/utils/EstimatedHistogram.java     |   2 +-
 ...DecayingEstimatedHistogramReservoirTest.java | 381 +++++++++++++
 7 files changed, 936 insertions(+), 116 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 232203e..05059cc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.8
+ * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
index 6fdb2ff..8e5671b 100644
--- a/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
+++ b/src/java/org/apache/cassandra/metrics/CassandraMetricsRegistry.java
@@ -60,7 +60,7 @@ public class CassandraMetricsRegistry extends MetricRegistry
 
     public Histogram histogram(MetricName name, boolean considerZeroes)
     {
-        Histogram histogram = register(name, new ClearableHistogram(new EstimatedHistogramReservoir(considerZeroes)));
+        Histogram histogram = register(name, new ClearableHistogram(new DecayingEstimatedHistogramReservoir(considerZeroes)));
         registerMBean(histogram, name.getMBeanName());
 
         return histogram;
@@ -68,7 +68,7 @@ public class CassandraMetricsRegistry extends MetricRegistry
 
     public Timer timer(MetricName name)
     {
-        Timer timer = register(name, new Timer(new EstimatedHistogramReservoir(false)));
+        Timer timer = register(name, new Timer(new DecayingEstimatedHistogramReservoir()));
         registerMBean(timer, name.getMBeanName());
 
         return timer;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/metrics/ClearableHistogram.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/metrics/ClearableHistogram.java b/src/java/org/apache/cassandra/metrics/ClearableHistogram.java
index 85f2fa9..4a081d8 100644
--- a/src/java/org/apache/cassandra/metrics/ClearableHistogram.java
+++ b/src/java/org/apache/cassandra/metrics/ClearableHistogram.java
@@ -26,14 +26,14 @@ import com.codahale.metrics.Histogram;
  */
 public class ClearableHistogram extends Histogram
 {
-    private final EstimatedHistogramReservoir reservoirRef;
+    private final DecayingEstimatedHistogramReservoir reservoirRef;
 
     /**
      * Creates a new {@link com.codahale.metrics.Histogram} with the given reservoir.
      *
      * @param reservoir the reservoir to create a histogram from
      */
-    public ClearableHistogram(EstimatedHistogramReservoir reservoir)
+    public ClearableHistogram(DecayingEstimatedHistogramReservoir reservoir)
     {
         super(reservoir);
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoir.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoir.java b/src/java/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoir.java
new file mode 100644
index 0000000..14a4366
--- /dev/null
+++ b/src/java/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoir.java
@@ -0,0 +1,549 @@
+/*
+ * 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.metrics;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicLongArray;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import com.codahale.metrics.Clock;
+import com.codahale.metrics.Reservoir;
+import com.codahale.metrics.Snapshot;
+import org.apache.cassandra.utils.EstimatedHistogram;
+
+/**
+ * A decaying histogram reservoir where values collected during each minute will be twice as significant as the values
+ * collected in the previous minute. Measured values are collected in variable sized buckets, using small buckets in the
+ * lower range and larger buckets in the upper range. Use this histogram when you want to know if the distribution of
+ * the underlying data stream has changed recently and you want high resolution on values in the lower range.
+ *
+ * The histogram use forward decay [1] to make recent values more significant. The forward decay factor will be doubled
+ * every minute (half-life time set to 60 seconds) [2]. The forward decay landmark is reset every 30 minutes (or at
+ * first read/update after 30 minutes). During landmark reset, updates and reads in the reservoir will be blocked in a
+ * fashion similar to the one used in the metrics library [3]. The 30 minute rescale interval is used based on the
+ * assumption that in an extreme case we would have to collect a metric 1M times for a single bucket each second. By the
+ * end of the 30:th minute all collected values will roughly add up to 1.000.000 * 60 * pow(2, 30) which can be
+ * represented with 56 bits giving us some head room in a signed 64 bit long.
+ *
+ * Internally two reservoirs are maintained, one with decay and one without decay. All public getters in a {@Snapshot}
+ * will expose the decay functionality with the exception of the {@link Snapshot#getValues()} which will return values
+ * from the reservoir without decay. This makes it possible for the caller to maintain precise deltas in an interval of
+ * its choise.
+ *
+ * The bucket size starts at 1 and grows by 1.2 each time (rounding and removing duplicates). It goes from 1 to around
+ * 18T by default (creating 164+1 buckets), which will give a timing resolution from microseconds to roughly 210 days,
+ * with less precision as the numbers get larger.
+ *
+ * The series of values to which the counts in `decayingBuckets` correspond:
+ * 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 17, 20, 24, 29, 35, 42, 50, 60, 72 etc.
+ * Thus, a `decayingBuckets` of [0, 0, 1, 10] would mean we had seen 1 value of 3 and 10 values of 4.
+ *
+ * Each bucket represents values from (previous bucket offset, current offset].
+ *
+ * [1]: http://dimacs.rutgers.edu/~graham/pubs/papers/fwddecay.pdf
+ * [2]: https://en.wikipedia.org/wiki/Half-life
+ * [3]: https://github.com/dropwizard/metrics/blob/v3.1.2/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java
+ */
+public class DecayingEstimatedHistogramReservoir implements Reservoir
+{
+    /**
+     * The default number of decayingBuckets. Use this bucket count to reduce memory allocation for bucket offsets.
+     */
+    public static final int DEFAULT_BUCKET_COUNT = 164;
+    public static final boolean DEFAULT_ZERO_CONSIDERATION = false;
+
+    // The offsets used with a default sized bucket array without a separate bucket for zero values.
+    public static final long[] DEFAULT_WITHOUT_ZERO_BUCKET_OFFSETS = EstimatedHistogram.newOffsets(DEFAULT_BUCKET_COUNT, false);
+
+    // The offsets used with a default sized bucket array with a separate bucket for zero values.
+    public static final long[] DEFAULT_WITH_ZERO_BUCKET_OFFSETS = EstimatedHistogram.newOffsets(DEFAULT_BUCKET_COUNT, true);
+
+    // Represents the bucket offset as created by {@link EstimatedHistogram#newOffsets()}
+    private final long[] bucketOffsets;
+
+    // decayingBuckets and buckets are one element longer than bucketOffsets -- the last element is values greater than the last offset
+    private final AtomicLongArray decayingBuckets;
+    private final AtomicLongArray buckets;
+
+    public static final long HALF_TIME_IN_S = 60L;
+    public static final double MEAN_LIFETIME_IN_S = HALF_TIME_IN_S / Math.log(2.0);
+    public static final long LANDMARK_RESET_INTERVAL_IN_MS = 30L * 60L * 1000L;
+
+    private final AtomicBoolean rescaling = new AtomicBoolean(false);
+    private volatile long decayLandmark;
+
+    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+    // Wrapper around System.nanoTime() to simplify unit testing.
+    private final Clock clock;
+
+
+    /**
+     * Construct a decaying histogram with default number of buckets and without considering zeroes.
+     */
+    public DecayingEstimatedHistogramReservoir()
+    {
+        this(DEFAULT_ZERO_CONSIDERATION, DEFAULT_BUCKET_COUNT, Clock.defaultClock());
+    }
+
+    /**
+     * Construct a decaying histogram with default number of buckets.
+     *
+     * @param considerZeroes when true, 0-value measurements in a separate bucket, otherwise they will be collected in
+     *                       same bucket as 1-value measurements
+     */
+    public DecayingEstimatedHistogramReservoir(boolean considerZeroes)
+    {
+        this(considerZeroes, DEFAULT_BUCKET_COUNT, Clock.defaultClock());
+    }
+
+    /**
+     * Construct a decaying histogram.
+     *
+     * @param considerZeroes when true, 0-value measurements in a separate bucket, otherwise they will be collected in
+     *                       same bucket as 1-value measurements
+     * @param bucketCount number of buckets used to collect measured values
+     */
+    public DecayingEstimatedHistogramReservoir(boolean considerZeroes, int bucketCount)
+    {
+        this(considerZeroes, bucketCount, Clock.defaultClock());
+    }
+
+    @VisibleForTesting
+    DecayingEstimatedHistogramReservoir(boolean considerZeroes, int bucketCount, Clock clock)
+    {
+        if (bucketCount == DEFAULT_BUCKET_COUNT)
+        {
+            if (considerZeroes == true)
+            {
+                bucketOffsets = DEFAULT_WITH_ZERO_BUCKET_OFFSETS;
+            }
+            else
+            {
+                bucketOffsets = DEFAULT_WITHOUT_ZERO_BUCKET_OFFSETS;
+            }
+        }
+        else
+        {
+            bucketOffsets = EstimatedHistogram.newOffsets(bucketCount, considerZeroes);
+        }
+        decayingBuckets = new AtomicLongArray(bucketOffsets.length + 1);
+        buckets = new AtomicLongArray(bucketOffsets.length + 1);
+        this.clock = clock;
+        decayLandmark = clock.getTime();
+    }
+
+    /**
+     * Increments the count of the bucket closest to n, rounding UP.
+     *
+     * @param value the data point to add to the histogram
+     */
+    public void update(long value)
+    {
+        long now = clock.getTime();
+        rescaleIfNeeded(now);
+
+        int index = Arrays.binarySearch(bucketOffsets, value);
+        if (index < 0)
+        {
+            // inexact match, take the first bucket higher than n
+            index = -index - 1;
+        }
+        // else exact match; we're good
+
+        lockForRegularUsage();
+
+        try
+        {
+            decayingBuckets.getAndAdd(index, forwardDecayWeight(now));
+        }
+        finally
+        {
+            unlockForRegularUsage();
+        }
+
+        buckets.getAndIncrement(index);
+    }
+
+    private long forwardDecayWeight(long now)
+    {
+        return Math.round(Math.exp(((now - decayLandmark) / 1000L) / MEAN_LIFETIME_IN_S));
+    }
+
+    /**
+     * Return the number of buckets where recorded values are stored.
+     *
+     * This method does not return the number of recorded values as suggested by the {@link Reservoir} interface.
+     *
+     * @return the number of buckets
+     */
+    public int size()
+    {
+        return decayingBuckets.length();
+    }
+
+    /**
+     * Returns a snapshot of the decaying values in this reservoir.
+     *
+     * Non-decaying reservoir will not be included in the snapshot.
+     *
+     * @return the snapshot
+     */
+    public Snapshot getSnapshot()
+    {
+        rescaleIfNeeded();
+
+        lockForRegularUsage();
+
+        try
+        {
+            return new EstimatedHistogramReservoirSnapshot(this);
+        }
+        finally
+        {
+            unlockForRegularUsage();
+        }
+    }
+
+    /**
+     * @return true if this histogram has overflowed -- that is, a value larger than our largest bucket could bound was added
+     */
+    @VisibleForTesting
+    boolean isOverflowed()
+    {
+        return decayingBuckets.get(decayingBuckets.length() - 1) > 0;
+    }
+
+    private void rescaleIfNeeded()
+    {
+        rescaleIfNeeded(clock.getTime());
+    }
+
+    private void rescaleIfNeeded(long now)
+    {
+        if (needRescale(now))
+        {
+            if (rescaling.compareAndSet(false, true))
+            {
+                try
+                {
+                    rescale(now);
+                }
+                finally
+                {
+                    rescaling.set(false);
+                }
+            }
+        }
+    }
+
+    private void rescale(long now)
+    {
+        // Check again to make sure that another thread didn't complete rescale already
+        if (needRescale(now))
+        {
+            lockForRescale();
+
+            try
+            {
+                final long rescaleFactor = forwardDecayWeight(now);
+                decayLandmark = now;
+
+                final int bucketCount = decayingBuckets.length();
+                for (int i = 0; i < bucketCount; i++)
+                {
+                    long newValue = Math.round((decayingBuckets.get(i) / rescaleFactor));
+                    decayingBuckets.set(i, newValue);
+                }
+            }
+            finally
+            {
+                unlockForRescale();
+            }
+        }
+    }
+
+    private boolean needRescale(long now)
+    {
+        return (now - decayLandmark) > LANDMARK_RESET_INTERVAL_IN_MS;
+    }
+
+    @VisibleForTesting
+    public void clear()
+    {
+        lockForRescale();
+
+        try
+        {
+            final int bucketCount = decayingBuckets.length();
+            for (int i = 0; i < bucketCount; i++)
+            {
+                decayingBuckets.set(i, 0L);
+                buckets.set(i, 0L);
+            }
+        }
+        finally
+        {
+            unlockForRescale();
+        }
+    }
+
+    private void lockForRegularUsage()
+    {
+        this.lock.readLock().lock();
+    }
+
+    private void unlockForRegularUsage()
+    {
+        this.lock.readLock().unlock();
+    }
+
+    private void lockForRescale()
+    {
+        this.lock.writeLock().lock();
+    }
+
+    private void unlockForRescale()
+    {
+        this.lock.writeLock().unlock();
+    }
+
+
+    private static final Charset UTF_8 = Charset.forName("UTF-8");
+
+    /**
+     * Represents a snapshot of the decaying histogram.
+     *
+     * The decaying buckets are copied into a snapshot array to give a consistent view for all getters. However, the
+     * copy is made without a write-lock and so other threads may change the buckets while the array is copied,
+     * probably causign a slight skew up in the quantiles and mean values.
+     *
+     * The decaying buckets will be used for quantile calculations and mean values, but the non decaying buckets will be
+     * exposed for calls to {@link Snapshot#getValues()}.
+     */
+    private class EstimatedHistogramReservoirSnapshot extends Snapshot
+    {
+        private final long[] decayingBuckets;
+
+        public EstimatedHistogramReservoirSnapshot(DecayingEstimatedHistogramReservoir reservoir)
+        {
+            final int length = reservoir.decayingBuckets.length();
+
+            this.decayingBuckets = new long[length];
+
+            for (int i = 0; i < length; i++)
+                this.decayingBuckets[i] = reservoir.decayingBuckets.get(i);
+        }
+
+        /**
+         * Get the estimated value at the specified quantile in the distribution.
+         *
+         * @param quantile the quantile specified as a value between 0.0 (zero) and 1.0 (one)
+         * @return estimated value at given quantile
+         * @throws IllegalStateException in case the histogram overflowed
+         */
+        public double getValue(double quantile)
+        {
+            assert quantile >= 0 && quantile <= 1.0;
+
+            final int lastBucket = decayingBuckets.length - 1;
+
+            if (decayingBuckets[lastBucket] > 0)
+                throw new IllegalStateException("Unable to compute when histogram overflowed");
+
+            final long qcount = (long) Math.ceil(count() * quantile);
+            if (qcount == 0)
+                return 0;
+
+            long elements = 0;
+            for (int i = 0; i < lastBucket; i++)
+            {
+                elements += decayingBuckets[i];
+                if (elements >= qcount)
+                    return bucketOffsets[i];
+            }
+            return 0;
+        }
+
+        /**
+         * Will return a snapshot of the non-decaying buckets.
+         *
+         * The values returned will not be consistent with the quantile and mean values. The caller must be aware of the
+         * offsets created by {@link EstimatedHistogram#getBucketOffsets()} to make use of the values returned.
+         *
+         * @return a snapshot of the non-decaying buckets.
+         */
+        public long[] getValues()
+        {
+            final int length = buckets.length();
+
+            long[] values = new long[length];
+
+            for (int i = 0; i < length; i++)
+                values[i] = buckets.get(i);
+
+            return values;
+        }
+
+        /**
+         * Return the number of buckets where recorded values are stored.
+         *
+         * This method does not return the number of recorded values as suggested by the {@link Snapshot} interface.
+         *
+         * @return the number of buckets
+         */
+        public int size()
+        {
+            return decayingBuckets.length;
+        }
+
+        /**
+         * Return the number of registered values taking forward decay into account.
+         *
+         * @return the sum of all bucket values
+         */
+        private long count()
+        {
+            long sum = 0L;
+            for (int i = 0; i < decayingBuckets.length; i++)
+                sum += decayingBuckets[i];
+            return sum;
+        }
+
+        /**
+         * Get the estimated max-value that could have been added to this reservoir.
+         *
+         * As values are collected in variable sized buckets, the actual max value recored in the reservoir may be less
+         * than the value returned.
+         *
+         * @return the largest value that could have been added to this reservoir, or Long.MAX_VALUE if the reservoir
+         * overflowed
+         */
+        public long getMax()
+        {
+            final int lastBucket = decayingBuckets.length - 1;
+
+            if (decayingBuckets[lastBucket] > 0)
+                return Long.MAX_VALUE;
+
+            for (int i = lastBucket - 1; i >= 0; i--)
+            {
+                if (decayingBuckets[i] > 0)
+                    return bucketOffsets[i];
+            }
+            return 0;
+        }
+
+        /**
+         * Get the estimated mean value in the distribution.
+         *
+         * @return the mean histogram value (average of bucket offsets, weighted by count)
+         * @throws IllegalStateException if any values were greater than the largest bucket threshold
+         */
+        public double getMean()
+        {
+            final int lastBucket = decayingBuckets.length - 1;
+
+            if (decayingBuckets[lastBucket] > 0)
+                throw new IllegalStateException("Unable to compute when histogram overflowed");
+
+            long elements = 0;
+            long sum = 0;
+            for (int i = 0; i < lastBucket; i++)
+            {
+                long bCount = decayingBuckets[i];
+                elements += bCount;
+                sum += bCount * bucketOffsets[i];
+            }
+
+            return (double) sum / elements;
+        }
+
+        /**
+         * Get the estimated min-value that could have been added to this reservoir.
+         *
+         * As values are collected in variable sized buckets, the actual min value recored in the reservoir may be
+         * higher than the value returned.
+         *
+         * @return the smallest value that could have been added to this reservoir
+         */
+        public long getMin()
+        {
+            for (int i = 0; i < decayingBuckets.length; i++)
+            {
+                if (decayingBuckets[i] > 0)
+                    return i == 0 ? 0 : 1 + bucketOffsets[i - 1];
+            }
+            return 0;
+        }
+
+        /**
+         * Get the estimated standard deviation of the values added to this reservoir.
+         *
+         * As values are collected in variable sized buckets, the actual deviation may be more or less than the value
+         * returned.
+         *
+         * @return an estimate of the standard deviation
+         */
+        public double getStdDev()
+        {
+            final int lastBucket = decayingBuckets.length - 1;
+
+            if (decayingBuckets[lastBucket] > 0)
+                throw new IllegalStateException("Unable to compute when histogram overflowed");
+
+            final long count = count();
+
+            if(count <= 1) {
+                return 0.0D;
+            } else {
+                double mean = this.getMean();
+                double sum = 0.0D;
+
+                for(int i = 0; i < lastBucket; ++i) {
+                    long value = bucketOffsets[i];
+                    double diff = (double)value - mean;
+                    sum += diff * diff * decayingBuckets[i];
+                }
+
+                return Math.sqrt(sum / (double)(count - 1));
+            }
+        }
+
+        public void dump(OutputStream output)
+        {
+            try (PrintWriter out = new PrintWriter(new OutputStreamWriter(output, UTF_8)))
+            {
+                int length = decayingBuckets.length;
+
+                for(int i = 0; i < length; ++i) {
+                    out.printf("%d%n", decayingBuckets[i]);
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java b/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java
deleted file mode 100644
index 29baad8..0000000
--- a/src/java/org/apache/cassandra/metrics/EstimatedHistogramReservoir.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.cassandra.metrics;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import com.codahale.metrics.Reservoir;
-import com.codahale.metrics.Snapshot;
-import com.codahale.metrics.UniformSnapshot;
-import org.apache.cassandra.utils.EstimatedHistogram;
-
-/**
- * Allows our Histogram implementation to be used by the metrics library.
- *
- * Default buckets allows nanosecond timings.
- */
-public class EstimatedHistogramReservoir implements Reservoir
-{
-    EstimatedHistogram histogram;
-
-    // Default to >4 hours of in nanoseconds of buckets
-    public EstimatedHistogramReservoir(boolean considerZeroes)
-    {
-        this(164, considerZeroes);
-    }
-
-    public EstimatedHistogramReservoir(int numBuckets, boolean considerZeroes)
-    {
-        histogram = new EstimatedHistogram(numBuckets, considerZeroes);
-    }
-
-    @Override
-    public int size()
-    {
-        return histogram.getBucketOffsets().length + 1;
-    }
-
-    @Override
-    public void update(long value)
-    {
-        histogram.add(value);
-    }
-
-    @Override
-    public Snapshot getSnapshot()
-    {
-        return new HistogramSnapshot(histogram);
-    }
-
-    @VisibleForTesting
-    public void clear()
-    {
-        histogram.getBuckets(true);
-    }
-
-    static class HistogramSnapshot extends UniformSnapshot
-    {
-        EstimatedHistogram histogram;
-
-        public HistogramSnapshot(EstimatedHistogram histogram)
-        {
-            super(histogram.getBuckets(false));
-
-            this.histogram = histogram;
-        }
-
-        @Override
-        public double getValue(double quantile)
-        {
-            return histogram.percentile(quantile);
-        }
-
-        @Override
-        public long getMax()
-        {
-            return histogram.max();
-        }
-
-        @Override
-        public long getMin()
-        {
-            return histogram.min();
-        }
-
-        @Override
-        public double getMean()
-        {
-            return histogram.rawMean();
-        }
-
-        @Override
-        public long[] getValues() {
-            return histogram.getBuckets(false);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/EstimatedHistogram.java b/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
index 36048fb..1a48039 100644
--- a/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
+++ b/src/java/org/apache/cassandra/utils/EstimatedHistogram.java
@@ -85,7 +85,7 @@ public class EstimatedHistogram
         buckets = new AtomicLongArray(bucketData);
     }
 
-    private static long[] newOffsets(int size, boolean considerZeroes)
+    public static long[] newOffsets(int size, boolean considerZeroes)
     {
         long[] result = new long[size + (considerZeroes ? 1 : 0)];
         int i = 0;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2e902596/test/unit/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoirTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoirTest.java b/test/unit/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoirTest.java
new file mode 100644
index 0000000..f2d817f
--- /dev/null
+++ b/test/unit/org/apache/cassandra/metrics/DecayingEstimatedHistogramReservoirTest.java
@@ -0,0 +1,381 @@
+/*
+ * 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.metrics;
+
+import org.junit.Test;
+
+import com.codahale.metrics.Clock;
+import com.codahale.metrics.Snapshot;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+public class DecayingEstimatedHistogramReservoirTest
+{
+    private static final double DOUBLE_ASSERT_DELTA = 0;
+
+    @Test
+    public void testSimple()
+    {
+        {
+            // 0 and 1 map to the same, first bucket
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir();
+            histogram.update(0);
+            assertEquals(1, histogram.getSnapshot().getValues()[0]);
+            histogram.update(1);
+            assertEquals(2, histogram.getSnapshot().getValues()[0]);
+        }
+        {
+            // 0 and 1 map to different buckets
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(true, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT);
+            histogram.update(0);
+            assertEquals(1, histogram.getSnapshot().getValues()[0]);
+            histogram.update(1);
+            Snapshot snapshot = histogram.getSnapshot();
+            assertEquals(1, snapshot.getValues()[0]);
+            assertEquals(1, snapshot.getValues()[1]);
+        }
+    }
+
+    @Test
+    public void testOverflow()
+    {
+        DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, 1);
+        histogram.update(100);
+        assert histogram.isOverflowed();
+        assertEquals(Long.MAX_VALUE, histogram.getSnapshot().getMax());
+    }
+
+    @Test
+    public void testMinMax()
+    {
+        DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir();
+        histogram.update(16);
+        Snapshot snapshot = histogram.getSnapshot();
+        assertEquals(15, snapshot.getMin());
+        assertEquals(17, snapshot.getMax());
+    }
+
+    @Test
+    public void testMean()
+    {
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+            for (int i = 0; i < 40; i++)
+                histogram.update(0);
+            for (int i = 0; i < 20; i++)
+                histogram.update(1);
+            for (int i = 0; i < 10; i++)
+                histogram.update(2);
+            assertEquals(1.14D, histogram.getSnapshot().getMean(), 0.1D);
+        }
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(true, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+            for (int i = 0; i < 40; i++)
+                histogram.update(0);
+            for (int i = 0; i < 20; i++)
+                histogram.update(1);
+            for (int i = 0; i < 10; i++)
+                histogram.update(2);
+            assertEquals(0.57D, histogram.getSnapshot().getMean(), 0.1D);
+        }
+    }
+
+    @Test
+    public void testStdDev()
+    {
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+            for (int i = 0; i < 20; i++)
+                histogram.update(10);
+            for (int i = 0; i < 40; i++)
+                histogram.update(20);
+            for (int i = 0; i < 20; i++)
+                histogram.update(30);
+
+            Snapshot snapshot = histogram.getSnapshot();
+            assertEquals(20.0D, snapshot.getMean(), 2.0D);
+            assertEquals(7.07D, snapshot.getStdDev(), 2.0D);
+        }
+    }
+
+    @Test
+    public void testFindingCorrectBuckets()
+    {
+        TestClock clock = new TestClock();
+
+        DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, 90, clock);
+        histogram.update(23282687);
+        assertFalse(histogram.isOverflowed());
+        assertEquals(1, histogram.getSnapshot().getValues()[89]);
+
+        histogram.update(9);
+        assertEquals(1, histogram.getSnapshot().getValues()[8]);
+
+        histogram.update(21);
+        histogram.update(22);
+        Snapshot snapshot = histogram.getSnapshot();
+        assertEquals(2, snapshot.getValues()[13]);
+        assertEquals(6277304.5D, snapshot.getMean(), DOUBLE_ASSERT_DELTA);
+    }
+
+    @Test
+    public void testPercentile()
+    {
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+            // percentile of empty histogram is 0
+            assertEquals(0D, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+
+            histogram.update(1);
+            // percentile of a histogram with one element should be that element
+            assertEquals(1D, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+
+            histogram.update(10);
+            assertEquals(10D, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+        }
+
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+
+            histogram.update(1);
+            histogram.update(2);
+            histogram.update(3);
+            histogram.update(4);
+            histogram.update(5);
+
+            Snapshot snapshot = histogram.getSnapshot();
+            assertEquals(0, snapshot.getValue(0.00), DOUBLE_ASSERT_DELTA);
+            assertEquals(3, snapshot.getValue(0.50), DOUBLE_ASSERT_DELTA);
+            assertEquals(3, snapshot.getValue(0.60), DOUBLE_ASSERT_DELTA);
+            assertEquals(5, snapshot.getValue(1.00), DOUBLE_ASSERT_DELTA);
+        }
+
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+
+            for (int i = 11; i <= 20; i++)
+                histogram.update(i);
+
+            // Right now the histogram looks like:
+            //    10   12   14   17   20
+            //     0    2    2    3    3
+            // %:  0   20   40   70  100
+            Snapshot snapshot = histogram.getSnapshot();
+            assertEquals(12, snapshot.getValue(0.01), DOUBLE_ASSERT_DELTA);
+            assertEquals(14, snapshot.getValue(0.30), DOUBLE_ASSERT_DELTA);
+            assertEquals(17, snapshot.getValue(0.50), DOUBLE_ASSERT_DELTA);
+            assertEquals(17, snapshot.getValue(0.60), DOUBLE_ASSERT_DELTA);
+            assertEquals(20, snapshot.getValue(0.80), DOUBLE_ASSERT_DELTA);
+        }
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(true, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+            histogram.update(0);
+            histogram.update(0);
+            histogram.update(1);
+
+            Snapshot snapshot = histogram.getSnapshot();
+            assertEquals(0, snapshot.getValue(0.5), DOUBLE_ASSERT_DELTA);
+            assertEquals(1, snapshot.getValue(0.99), DOUBLE_ASSERT_DELTA);
+        }
+    }
+
+
+    @Test
+    public void testDecayingPercentile()
+    {
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+            // percentile of empty histogram is 0
+            assertEquals(0, histogram.getSnapshot().getValue(1.0), DOUBLE_ASSERT_DELTA);
+
+            for (int v = 1; v <= 100; v++)
+            {
+                for (int i = 0; i < 10_000; i++)
+                {
+                    histogram.update(v);
+                }
+            }
+
+            Snapshot snapshot = histogram.getSnapshot();
+            assertEstimatedQuantile(05, snapshot.getValue(0.05));
+            assertEstimatedQuantile(20, snapshot.getValue(0.20));
+            assertEstimatedQuantile(40, snapshot.getValue(0.40));
+            assertEstimatedQuantile(99, snapshot.getValue(0.99));
+
+            clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+            snapshot = histogram.getSnapshot();
+            assertEstimatedQuantile(05, snapshot.getValue(0.05));
+            assertEstimatedQuantile(20, snapshot.getValue(0.20));
+            assertEstimatedQuantile(40, snapshot.getValue(0.40));
+            assertEstimatedQuantile(99, snapshot.getValue(0.99));
+
+            for (int v = 1; v <= 50; v++)
+            {
+                for (int i = 0; i < 10_000; i++)
+                {
+                    histogram.update(v);
+                }
+            }
+
+            snapshot = histogram.getSnapshot();
+            assertEstimatedQuantile(04, snapshot.getValue(0.05));
+            assertEstimatedQuantile(14, snapshot.getValue(0.20));
+            assertEstimatedQuantile(27, snapshot.getValue(0.40));
+            assertEstimatedQuantile(98, snapshot.getValue(0.99));
+
+            clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+            snapshot = histogram.getSnapshot();
+            assertEstimatedQuantile(04, snapshot.getValue(0.05));
+            assertEstimatedQuantile(14, snapshot.getValue(0.20));
+            assertEstimatedQuantile(27, snapshot.getValue(0.40));
+            assertEstimatedQuantile(98, snapshot.getValue(0.99));
+
+            for (int v = 1; v <= 50; v++)
+            {
+                for (int i = 0; i < 10_000; i++)
+                {
+                    histogram.update(v);
+                }
+            }
+
+            snapshot = histogram.getSnapshot();
+            assertEstimatedQuantile(03, snapshot.getValue(0.05));
+            assertEstimatedQuantile(12, snapshot.getValue(0.20));
+            assertEstimatedQuantile(23, snapshot.getValue(0.40));
+            assertEstimatedQuantile(96, snapshot.getValue(0.99));
+
+            clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+            snapshot = histogram.getSnapshot();
+            assertEstimatedQuantile(03, snapshot.getValue(0.05));
+            assertEstimatedQuantile(12, snapshot.getValue(0.20));
+            assertEstimatedQuantile(23, snapshot.getValue(0.40));
+            assertEstimatedQuantile(96, snapshot.getValue(0.99));
+
+            for (int v = 11; v <= 20; v++)
+            {
+                for (int i = 0; i < 5_000; i++)
+                {
+                    histogram.update(v);
+                }
+            }
+
+            snapshot = histogram.getSnapshot();
+            assertEstimatedQuantile(04, snapshot.getValue(0.05));
+            assertEstimatedQuantile(12, snapshot.getValue(0.20));
+            assertEstimatedQuantile(20, snapshot.getValue(0.40));
+            assertEstimatedQuantile(95, snapshot.getValue(0.99));
+
+            clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+            snapshot = histogram.getSnapshot();
+            assertEstimatedQuantile(04, snapshot.getValue(0.05));
+            assertEstimatedQuantile(12, snapshot.getValue(0.20));
+            assertEstimatedQuantile(20, snapshot.getValue(0.40));
+            assertEstimatedQuantile(95, snapshot.getValue(0.99));
+
+        }
+
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+            // percentile of empty histogram is 0
+            assertEquals(0, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+
+            for (int m = 0; m < 40; m++)
+            {
+                for (int i = 0; i < 1_000_000; i++)
+                {
+                    histogram.update(2);
+                }
+                // percentile of a histogram with one element should be that element
+                clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+                assertEquals(2, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+            }
+
+            clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S * 100);
+            assertEquals(0, histogram.getSnapshot().getValue(0.99), DOUBLE_ASSERT_DELTA);
+        }
+
+        {
+            TestClock clock = new TestClock();
+
+            DecayingEstimatedHistogramReservoir histogram = new DecayingEstimatedHistogramReservoir(DecayingEstimatedHistogramReservoir.DEFAULT_ZERO_CONSIDERATION, DecayingEstimatedHistogramReservoir.DEFAULT_BUCKET_COUNT, clock);
+
+            histogram.update(20);
+            histogram.update(21);
+            histogram.update(22);
+            Snapshot snapshot = histogram.getSnapshot();
+            assertEquals(1, snapshot.getValues()[12]);
+            assertEquals(2, snapshot.getValues()[13]);
+
+            clock.addSeconds(DecayingEstimatedHistogramReservoir.HALF_TIME_IN_S);
+
+            histogram.update(20);
+            histogram.update(21);
+            histogram.update(22);
+            snapshot = histogram.getSnapshot();
+            assertEquals(2, snapshot.getValues()[12]);
+            assertEquals(4, snapshot.getValues()[13]);
+        }
+    }
+
+    private void assertEstimatedQuantile(long expectedValue, double actualValue)
+    {
+        assertTrue("Expected at least [" + expectedValue + "] but actual is [" + actualValue + "]", actualValue >= expectedValue);
+        assertTrue("Expected less than [" + Math.round(expectedValue * 1.2) + "] but actual is [" + actualValue + "]", actualValue < Math.round(expectedValue * 1.2));
+    }
+
+    public class TestClock extends Clock {
+        private long tick = 0;
+
+        public void addSeconds(long seconds)
+        {
+            tick += seconds * 1_000_000_000L;
+        }
+
+        public long getTick()
+        {
+            return tick;
+        }
+
+        public long getTime()
+        {
+            return tick / 1_000_000L;
+        };
+    }
+}


[29/50] [abbrv] cassandra git commit: Merge branch 'cassandra-2.2' into cassandra-3.0

Posted by ms...@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/5fdb90e1
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5fdb90e1
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5fdb90e1

Branch: refs/heads/cassandra-3.8
Commit: 5fdb90e12b04fa4209993f609206e6920138d851
Parents: 5cef78a 91f7387
Author: Tyler Hobbs <ty...@gmail.com>
Authored: Fri Aug 12 18:29:36 2016 -0500
Committer: Tyler Hobbs <ty...@gmail.com>
Committed: Fri Aug 12 18:29:36 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 NEWS.txt                                        |  4 +--
 .../cassandra/config/DatabaseDescriptor.java    |  6 ++--
 .../cassandra/locator/Ec2MultiRegionSnitch.java |  6 +++-
 .../cassandra/service/StorageService.java       |  4 +--
 .../org/apache/cassandra/transport/Server.java  |  2 +-
 .../org/apache/cassandra/utils/FBUtilities.java | 20 ++++++++++++
 .../apache/cassandra/utils/FBUtilitiesTest.java | 32 ++++++++++++++++++++
 8 files changed, 67 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index cccb62d,394598a..7ba8370
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,40 -1,5 +1,41 @@@
 -2.2.8
 +3.0.9
 + * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
 + * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
 + * Backport CASSANDRA-12002 (CASSANDRA-12177)
 + * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
 + * Fix potential bad messaging service message for paged range reads
 +   within mixed-version 3.x clusters (CASSANDRA-12249)
 + * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
 + * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
 + * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
 + * Fix upgrade of super columns on thrift (CASSANDRA-12335)
 + * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
 + * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
 + * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
 + * Lost counter writes in compact table and static columns (CASSANDRA-12219)
 + * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
 + * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
 + * Add option to override compaction space check (CASSANDRA-12180)
 + * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
 + * Respond with v1/v2 protocol header when responding to driver that attempts
 +   to connect with too low of a protocol version (CASSANDRA-11464)
 + * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
 + * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
 + * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
 + * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
 + * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
 + * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
 + * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
 + * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
 + * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
 + * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
 + * Fix column ordering of results with static columns for Thrift requests in
 +   a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
 +   those static columns in query results (CASSANDRA-12123)
 + * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
 + * Fix EOF exception when altering column type (CASSANDRA-11820)
 +Merged from 2.2:
+  * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index 56dd6ea,f0712eb..6b8dfd3
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -13,55 -13,7 +13,55 @@@ restore snapshots created with the prev
  'sstableloader' tool. You can upgrade the file format of your snapshots
  using the provided 'sstableupgrade' tool.
  
 -2.2.7
 +3.0.8
 +=====
 +
 +Upgrading
 +---------
-    - Nothing specific to this release, but please see previous versions upgrading section,
-      especially if you are upgrading from 2.2.
++   - 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/5fdb90e1/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/src/java/org/apache/cassandra/service/StorageService.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/src/java/org/apache/cassandra/transport/Server.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/src/java/org/apache/cassandra/utils/FBUtilities.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/utils/FBUtilities.java
index d996c91,23a2c2e..ca2775f
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@@ -32,7 -34,9 +32,8 @@@ import java.util.zip.Checksum
  import javax.annotation.Nonnull;
  import javax.annotation.Nullable;
  
+ import com.google.common.annotations.VisibleForTesting;
  import com.google.common.base.Joiner;
 -import com.google.common.collect.AbstractIterator;
  import org.apache.commons.lang3.StringUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5fdb90e1/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
index 90c5f05,5b86252..acd68eb
--- a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
+++ b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
@@@ -18,7 -18,10 +18,8 @@@
  
  package org.apache.cassandra.utils;
  
 -import static org.junit.Assert.fail;
 -
  import java.io.IOException;
+ import java.net.InetAddress;
  import java.nio.ByteBuffer;
  import java.nio.charset.CharacterCodingException;
  import java.nio.charset.StandardCharsets;
@@@ -29,8 -31,10 +30,11 @@@ import org.junit.Test
  import java.util.Map;
  import java.util.TreeMap;
  
+ import org.apache.cassandra.config.Config;
+ import org.apache.cassandra.config.DatabaseDescriptor;
+ 
  import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.fail;
  
  public class FBUtilitiesTest
  {


[16/50] [abbrv] cassandra git commit: Fix potential resource leak in RMIServerSocketFactoryImpl

Posted by ms...@apache.org.
Fix potential resource leak in RMIServerSocketFactoryImpl

patch by Arunkumar M; reviewed by yukim for CASSANDRA-12331


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/19245e5f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/19245e5f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/19245e5f

Branch: refs/heads/cassandra-3.8
Commit: 19245e5faccdd4c1b926e94c38b7a7faad005453
Parents: de86ab1
Author: arunkumar-m <ar...@gmail.com>
Authored: Thu Aug 4 08:48:49 2016 -0700
Committer: Yuki Morishita <yu...@apache.org>
Committed: Thu Aug 11 11:01:32 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                            |  1 +
 .../cassandra/utils/RMIServerSocketFactoryImpl.java    | 13 +++++++++++--
 2 files changed, 12 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/19245e5f/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 3db2c77..0a1d406 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.9
+ * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
  * Backport CASSANDRA-12002 (CASSANDRA-12177)
  * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
  * Fix potential bad messaging service message for paged range reads

http://git-wip-us.apache.org/repos/asf/cassandra/blob/19245e5f/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java b/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
index 6d4ff72..6444a65 100644
--- a/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
+++ b/src/java/org/apache/cassandra/utils/RMIServerSocketFactoryImpl.java
@@ -23,6 +23,7 @@ package org.apache.cassandra.utils;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.ServerSocket;
+import java.net.SocketException;
 import java.rmi.server.RMIServerSocketFactory;
 import javax.net.ServerSocketFactory;
 
@@ -34,8 +35,16 @@ public class RMIServerSocketFactoryImpl implements RMIServerSocketFactory
     {
         ServerSocket socket = ServerSocketFactory.getDefault()
                                                  .createServerSocket(pPort, 0, InetAddress.getLoopbackAddress());
-        socket.setReuseAddress(true);
-        return socket;
+        try
+        {
+            socket.setReuseAddress(true);
+            return socket;
+        }
+        catch (SocketException e)
+        {
+            socket.close();
+            throw e;
+        }
     }
 
     public boolean equals(Object obj)


[22/50] [abbrv] cassandra git commit: Merge branch cassandra-2.2 into cassandra-3.0

Posted by ms...@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/c85749e2
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c85749e2
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c85749e2

Branch: refs/heads/cassandra-3.8
Commit: c85749e2bc5c65a03b00994565cd0b6b1a642e65
Parents: 2a828af e86d531
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Aug 12 15:34:01 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Aug 12 15:37:13 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../apache/cassandra/service/StorageProxy.java  | 49 +++++++++++++-------
 2 files changed, 34 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/c85749e2/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 3ce9f9e,ddc6720..959b967
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,39 -1,5 +1,40 @@@
 -2.2.8
 +3.0.9
 + * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
 + * Backport CASSANDRA-12002 (CASSANDRA-12177)
 + * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
 + * Fix potential bad messaging service message for paged range reads
 +   within mixed-version 3.x clusters (CASSANDRA-12249)
 + * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)
 + * NullPointerException during compaction on table with static columns (CASSANDRA-12336)
 + * Fixed ConcurrentModificationException when reading metrics in GraphiteReporter (CASSANDRA-11823)
 + * Fix upgrade of super columns on thrift (CASSANDRA-12335)
 + * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
 + * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
 + * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
 + * Lost counter writes in compact table and static columns (CASSANDRA-12219)
 + * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
 + * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
 + * Add option to override compaction space check (CASSANDRA-12180)
 + * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
 + * Respond with v1/v2 protocol header when responding to driver that attempts
 +   to connect with too low of a protocol version (CASSANDRA-11464)
 + * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
 + * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
 + * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
 + * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
 + * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
 + * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
 + * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
 + * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
 + * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
 + * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
 + * Fix column ordering of results with static columns for Thrift requests in
 +   a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
 +   those static columns in query results (CASSANDRA-12123)
 + * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
 + * Fix EOF exception when altering column type (CASSANDRA-11820)
 +Merged from 2.2:
+  * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c85749e2/src/java/org/apache/cassandra/service/StorageProxy.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/service/StorageProxy.java
index 483da67,7b7979d..8a151f2
--- a/src/java/org/apache/cassandra/service/StorageProxy.java
+++ b/src/java/org/apache/cassandra/service/StorageProxy.java
@@@ -1955,201 -1716,267 +1955,218 @@@ public class StorageProxy implements St
          }
      }
  
 -    public static List<Row> getRangeSlice(AbstractRangeCommand command, ConsistencyLevel consistency_level)
 -    throws UnavailableException, ReadFailureException, ReadTimeoutException
 +    private static class SingleRangeResponse extends AbstractIterator<RowIterator> implements PartitionIterator
      {
 -        Tracing.trace("Computing ranges to query");
 -        long startTime = System.nanoTime();
 +        private final ReadCallback handler;
 +        private PartitionIterator result;
  
 -        Keyspace keyspace = Keyspace.open(command.keyspace);
 -        List<Row> rows;
 -        // now scan until we have enough results
 -        try
 +        private SingleRangeResponse(ReadCallback handler)
          {
 -            int liveRowCount = 0;
 -            boolean countLiveRows = command.countCQL3Rows() || command.ignoredTombstonedPartitions();
 -            rows = new ArrayList<>();
 +            this.handler = handler;
 +        }
  
 -            // when dealing with LocalStrategy keyspaces, we can skip the range splitting and merging (which can be
 -            // expensive in clusters with vnodes)
 -            List<? extends AbstractBounds<RowPosition>> ranges;
 -            if (keyspace.getReplicationStrategy() instanceof LocalStrategy)
 -                ranges = command.keyRange.unwrap();
 -            else
 -                ranges = getRestrictedRanges(command.keyRange);
 -
 -            // determine the number of rows to be fetched and the concurrency factor
 -            int rowsToBeFetched = command.limit();
 -            int concurrencyFactor;
 -            if (command.requiresScanningAllRanges())
 -            {
 -                // all nodes must be queried
 -                rowsToBeFetched *= ranges.size();
 -                concurrencyFactor = ranges.size();
 -                logger.debug("Requested rows: {}, ranges.size(): {}; concurrent range requests: {}",
 -                             command.limit(),
 -                             ranges.size(),
 -                             concurrencyFactor);
 -                Tracing.trace("Submitting range requests on {} ranges with a concurrency of {}",
 -                              ranges.size(), concurrencyFactor);
 +        private void waitForResponse() throws ReadTimeoutException
 +        {
 +            if (result != null)
 +                return;
 +
 +            try
 +            {
 +                result = handler.get();
              }
 -            else
 +            catch (DigestMismatchException e)
              {
 -                // our estimate of how many result rows there will be per-range
 -                float resultRowsPerRange = estimateResultRowsPerRange(command, keyspace);
 -                // underestimate how many rows we will get per-range in order to increase the likelihood that we'll
 -                // fetch enough rows in the first round
 -                resultRowsPerRange -= resultRowsPerRange * CONCURRENT_SUBREQUESTS_MARGIN;
 -                concurrencyFactor = resultRowsPerRange == 0.0
 -                                  ? 1
 -                                  : Math.max(1, Math.min(ranges.size(), (int) Math.ceil(command.limit() / resultRowsPerRange)));
 -
 -                logger.trace("Estimated result rows per range: {}; requested rows: {}, ranges.size(): {}; concurrent range requests: {}",
 -                             resultRowsPerRange,
 -                             command.limit(),
 -                             ranges.size(),
 -                             concurrencyFactor);
 -                Tracing.trace("Submitting range requests on {} ranges with a concurrency of {} ({} rows per range expected)",
 -                              ranges.size(),
 -                              concurrencyFactor,
 -                              resultRowsPerRange);
 -            }
 -
 -            boolean haveSufficientRows = false;
 -            int i = 0;
 -            AbstractBounds<RowPosition> nextRange = null;
 -            List<InetAddress> nextEndpoints = null;
 -            List<InetAddress> nextFilteredEndpoints = null;
 -            while (i < ranges.size())
 -            {
 -                List<Pair<AbstractRangeCommand, ReadCallback<RangeSliceReply, Iterable<Row>>>> scanHandlers = new ArrayList<>(concurrencyFactor);
 -                int concurrentFetchStartingIndex = i;
 -                int concurrentRequests = 0;
 -                while ((i - concurrentFetchStartingIndex) < concurrencyFactor)
 -                {
 -                    AbstractBounds<RowPosition> range = nextRange == null
 -                                                      ? ranges.get(i)
 -                                                      : nextRange;
 -                    List<InetAddress> liveEndpoints = nextEndpoints == null
 -                                                    ? getLiveSortedEndpoints(keyspace, range.right)
 -                                                    : nextEndpoints;
 -                    List<InetAddress> filteredEndpoints = nextFilteredEndpoints == null
 -                                                        ? consistency_level.filterForQuery(keyspace, liveEndpoints)
 -                                                        : nextFilteredEndpoints;
 -                    ++i;
 -                    ++concurrentRequests;
 -
 -                    // getRestrictedRange has broken the queried range into per-[vnode] token ranges, but this doesn't take
 -                    // the replication factor into account. If the intersection of live endpoints for 2 consecutive ranges
 -                    // still meets the CL requirements, then we can merge both ranges into the same RangeSliceCommand.
 -                    while (i < ranges.size())
 -                    {
 -                        nextRange = ranges.get(i);
 -                        nextEndpoints = getLiveSortedEndpoints(keyspace, nextRange.right);
 -                        nextFilteredEndpoints = consistency_level.filterForQuery(keyspace, nextEndpoints);
 -
 -                        // If the current range right is the min token, we should stop merging because CFS.getRangeSlice
 -                        // don't know how to deal with a wrapping range.
 -                        // Note: it would be slightly more efficient to have CFS.getRangeSlice on the destination nodes unwraps
 -                        // the range if necessary and deal with it. However, we can't start sending wrapped range without breaking
 -                        // wire compatibility, so It's likely easier not to bother;
 -                        if (range.right.isMinimum())
 -                            break;
 -
 -                        List<InetAddress> merged = intersection(liveEndpoints, nextEndpoints);
 -
 -                        // Check if there is enough endpoint for the merge to be possible.
 -                        if (!consistency_level.isSufficientLiveNodes(keyspace, merged))
 -                            break;
 -
 -                        List<InetAddress> filteredMerged = consistency_level.filterForQuery(keyspace, merged);
 -
 -                        // Estimate whether merging will be a win or not
 -                        if (!DatabaseDescriptor.getEndpointSnitch().isWorthMergingForRangeQuery(filteredMerged, filteredEndpoints, nextFilteredEndpoints))
 -                            break;
 -
 -                        // If we get there, merge this range and the next one
 -                        range = range.withNewRight(nextRange.right);
 -                        liveEndpoints = merged;
 -                        filteredEndpoints = filteredMerged;
 -                        ++i;
 -                    }
 -
 -                    AbstractRangeCommand nodeCmd = command.forSubRange(range);
 +                throw new AssertionError(e); // no digests in range slices yet
 +            }
 +        }
  
 -                    // collect replies and resolve according to consistency level
 -                    RangeSliceResponseResolver resolver = new RangeSliceResponseResolver(nodeCmd.keyspace, command.timestamp);
 -                    List<InetAddress> minimalEndpoints = filteredEndpoints.subList(0, Math.min(filteredEndpoints.size(), consistency_level.blockFor(keyspace)));
 -                    ReadCallback<RangeSliceReply, Iterable<Row>> handler = new ReadCallback<>(resolver, consistency_level, nodeCmd, minimalEndpoints);
 -                    handler.assureSufficientLiveNodes();
 -                    resolver.setSources(filteredEndpoints);
 -                    if (filteredEndpoints.size() == 1
 -                        && filteredEndpoints.get(0).equals(FBUtilities.getBroadcastAddress()))
 -                    {
 -                        StageManager.getStage(Stage.READ).execute(new LocalRangeSliceRunnable(nodeCmd, handler));
 -                    }
 -                    else
 -                    {
 -                        MessageOut<? extends AbstractRangeCommand> message = nodeCmd.createMessage();
 -                        for (InetAddress endpoint : filteredEndpoints)
 -                        {
 -                            Tracing.trace("Enqueuing request to {}", endpoint);
 -                            MessagingService.instance().sendRRWithFailure(message, endpoint, handler);
 -                        }
 -                    }
 -                    scanHandlers.add(Pair.create(nodeCmd, handler));
 -                }
 -                Tracing.trace("Submitted {} concurrent range requests covering {} ranges", concurrentRequests, i - concurrentFetchStartingIndex);
 +        protected RowIterator computeNext()
 +        {
 +            waitForResponse();
 +            return result.hasNext() ? result.next() : endOfData();
 +        }
  
 -                List<AsyncOneResponse> repairResponses = new ArrayList<>();
 -                for (Pair<AbstractRangeCommand, ReadCallback<RangeSliceReply, Iterable<Row>>> cmdPairHandler : scanHandlers)
 -                {
 -                    ReadCallback<RangeSliceReply, Iterable<Row>> handler = cmdPairHandler.right;
 -                    RangeSliceResponseResolver resolver = (RangeSliceResponseResolver)handler.resolver;
 +        public void close()
 +        {
 +            if (result != null)
 +                result.close();
 +        }
 +    }
  
 -                    try
 -                    {
 -                        for (Row row : handler.get())
 -                        {
 -                            rows.add(row);
 -                            if (countLiveRows)
 -                                liveRowCount += row.getLiveCount(command.predicate, command.timestamp);
 -                        }
 -                        repairResponses.addAll(resolver.repairResults);
 -                    }
 -                    catch (ReadTimeoutException|ReadFailureException ex)
 -                    {
 -                        // we timed out or failed waiting for responses
 -                        int blockFor = consistency_level.blockFor(keyspace);
 -                        int responseCount = resolver.responses.size();
 -                        String gotData = responseCount > 0
 -                                         ? resolver.isDataPresent() ? " (including data)" : " (only digests)"
 -                                         : "";
 -
 -                        boolean isTimeout = ex instanceof ReadTimeoutException;
 -                        if (Tracing.isTracing())
 -                        {
 -                            Tracing.trace("{}; received {} of {} responses{} for range {} of {}",
 -                                          (isTimeout ? "Timed out" : "Failed"), responseCount, blockFor, gotData, i, ranges.size());
 -                        }
 -                        else if (logger.isDebugEnabled())
 -                        {
 -                            logger.debug("Range slice {}; received {} of {} responses{} for range {} of {}",
 -                                         (isTimeout ? "timeout" : "failure"), responseCount, blockFor, gotData, i, ranges.size());
 -                        }
 -                        throw ex;
 -                    }
 -                    catch (DigestMismatchException e)
 -                    {
 -                        throw new AssertionError(e); // no digests in range slices yet
 -                    }
 +    private static class RangeCommandIterator extends AbstractIterator<RowIterator> implements PartitionIterator
 +    {
 +        private final Iterator<RangeForQuery> ranges;
 +        private final int totalRangeCount;
 +        private final PartitionRangeReadCommand command;
 +        private final Keyspace keyspace;
 +        private final ConsistencyLevel consistency;
  
 -                    // if we're done, great, otherwise, move to the next range
 -                    int count = countLiveRows ? liveRowCount : rows.size();
 -                    if (count >= rowsToBeFetched)
 -                    {
 -                        haveSufficientRows = true;
 -                        break;
 -                    }
 -                }
 +        private final long startTime;
 +        private DataLimits.Counter counter;
 +        private PartitionIterator sentQueryIterator;
  
 -                try
 -                {
 -                    FBUtilities.waitOnFutures(repairResponses, DatabaseDescriptor.getWriteRpcTimeout());
 -                }
 -                catch (TimeoutException ex)
 -                {
 -                    // We got all responses, but timed out while repairing
 -                    int blockFor = consistency_level.blockFor(keyspace);
 -                    if (Tracing.isTracing())
 -                        Tracing.trace("Timed out while read-repairing after receiving all {} data and digest responses", blockFor);
 -                    else
 -                        logger.debug("Range slice timeout while read-repairing after receiving all {} data and digest responses", blockFor);
 -                    throw new ReadTimeoutException(consistency_level, blockFor-1, blockFor, true);
 -                }
 +        private int concurrencyFactor;
 +        // The two following "metric" are maintained to improve the concurrencyFactor
 +        // when it was not good enough initially.
 +        private int liveReturned;
 +        private int rangesQueried;
  
 -                if (haveSufficientRows)
 -                    return command.postReconciliationProcessing(rows);
 +        public RangeCommandIterator(RangeIterator ranges, PartitionRangeReadCommand command, int concurrencyFactor, Keyspace keyspace, ConsistencyLevel consistency)
 +        {
 +            this.command = command;
 +            this.concurrencyFactor = concurrencyFactor;
 +            this.startTime = System.nanoTime();
 +            this.ranges = new RangeMerger(ranges, keyspace, consistency);
 +            this.totalRangeCount = ranges.rangeCount();
 +            this.consistency = consistency;
 +            this.keyspace = keyspace;
 +        }
  
 -                // we didn't get enough rows in our concurrent fetch; recalculate our concurrency factor
 -                // based on the results we've seen so far (as long as we still have ranges left to query)
 -                if (i < ranges.size())
 +        public RowIterator computeNext()
 +        {
-             while (sentQueryIterator == null || !sentQueryIterator.hasNext())
++            try
 +            {
-                 // If we don't have more range to handle, we're done
-                 if (!ranges.hasNext())
-                     return endOfData();
- 
-                 // else, sends the next batch of concurrent queries (after having close the previous iterator)
-                 if (sentQueryIterator != null)
++                while (sentQueryIterator == null || !sentQueryIterator.hasNext())
                  {
-                     liveReturned += counter.counted();
-                     sentQueryIterator.close();
 -                    float fetchedRows = countLiveRows ? liveRowCount : rows.size();
 -                    float remainingRows = rowsToBeFetched - fetchedRows;
 -                    float actualRowsPerRange;
 -                    if (fetchedRows == 0.0)
 -                    {
 -                        // we haven't actually gotten any results, so query all remaining ranges at once
 -                        actualRowsPerRange = 0.0f;
 -                        concurrencyFactor = ranges.size() - i;
 -                    }
 -                    else
++                    // If we don't have more range to handle, we're done
++                    if (!ranges.hasNext())
++                        return endOfData();
 +
-                     // It's not the first batch of queries and we're not done, so we we can use what has been
-                     // returned so far to improve our rows-per-range estimate and update the concurrency accordingly
-                     updateConcurrencyFactor();
++                    // else, sends the next batch of concurrent queries (after having close the previous iterator)
++                    if (sentQueryIterator != null)
+                     {
 -                        actualRowsPerRange = fetchedRows / i;
 -                        concurrencyFactor = Math.max(1, Math.min(ranges.size() - i, Math.round(remainingRows / actualRowsPerRange)));
++                        liveReturned += counter.counted();
++                        sentQueryIterator.close();
++
++                        // It's not the first batch of queries and we're not done, so we we can use what has been
++                        // returned so far to improve our rows-per-range estimate and update the concurrency accordingly
++                        updateConcurrencyFactor();
+                     }
 -                    logger.trace("Didn't get enough response rows; actual rows per range: {}; remaining rows: {}, new concurrent requests: {}",
 -                                 actualRowsPerRange, (int) remainingRows, concurrencyFactor);
++                    sentQueryIterator = sendNextRequests();
                  }
-                 sentQueryIterator = sendNextRequests();
-             }
 +
-             return sentQueryIterator.next();
++                return sentQueryIterator.next();
++            }
++            catch (UnavailableException e)
++            {
++                rangeMetrics.unavailables.mark();
++                throw e;
++            }
++            catch (ReadTimeoutException e)
++            {
++                rangeMetrics.timeouts.mark();
++                throw e;
++            }
++            catch (ReadFailureException e)
++            {
++                rangeMetrics.failures.mark();
++                throw e;
+             }
          }
 -        catch (ReadTimeoutException e)
 +
 +        private void updateConcurrencyFactor()
          {
 -            rangeMetrics.timeouts.mark();
 -            throw e;
 +            if (liveReturned == 0)
 +            {
 +                // we haven't actually gotten any results, so query all remaining ranges at once
 +                concurrencyFactor = totalRangeCount - rangesQueried;
 +                return;
 +            }
 +
 +            // Otherwise, compute how many rows per range we got on average and pick a concurrency factor
 +            // that should allow us to fetch all remaining rows with the next batch of (concurrent) queries.
 +            int remainingRows = command.limits().count() - liveReturned;
 +            float rowsPerRange = (float)liveReturned / (float)rangesQueried;
 +            concurrencyFactor = Math.max(1, Math.min(totalRangeCount - rangesQueried, Math.round(remainingRows / rowsPerRange)));
 +            logger.trace("Didn't get enough response rows; actual rows per range: {}; remaining rows: {}, new concurrent requests: {}",
 +                         rowsPerRange, (int) remainingRows, concurrencyFactor);
          }
 -        catch (UnavailableException e)
 +
 +        private SingleRangeResponse query(RangeForQuery toQuery)
          {
 -            rangeMetrics.unavailables.mark();
 -            throw e;
 +            PartitionRangeReadCommand rangeCommand = command.forSubRange(toQuery.range);
 +
 +            DataResolver resolver = new DataResolver(keyspace, rangeCommand, consistency, toQuery.filteredEndpoints.size());
 +
 +            int blockFor = consistency.blockFor(keyspace);
 +            int minResponses = Math.min(toQuery.filteredEndpoints.size(), blockFor);
 +            List<InetAddress> minimalEndpoints = toQuery.filteredEndpoints.subList(0, minResponses);
 +            ReadCallback handler = new ReadCallback(resolver, consistency, rangeCommand, minimalEndpoints);
 +
 +            handler.assureSufficientLiveNodes();
 +
 +            if (toQuery.filteredEndpoints.size() == 1 && canDoLocalRequest(toQuery.filteredEndpoints.get(0)))
 +            {
 +                StageManager.getStage(Stage.READ).execute(new LocalReadRunnable(rangeCommand, handler));
 +            }
 +            else
 +            {
 +                for (InetAddress endpoint : toQuery.filteredEndpoints)
 +                {
 +                    MessageOut<ReadCommand> message = rangeCommand.createMessage(MessagingService.instance().getVersion(endpoint));
 +                    Tracing.trace("Enqueuing request to {}", endpoint);
 +                    MessagingService.instance().sendRRWithFailure(message, endpoint, handler);
 +                }
 +            }
 +
 +            return new SingleRangeResponse(handler);
          }
 -        catch (ReadFailureException e)
 +
 +        private PartitionIterator sendNextRequests()
          {
 -            rangeMetrics.failures.mark();
 -            throw e;
 +            List<PartitionIterator> concurrentQueries = new ArrayList<>(concurrencyFactor);
 +            for (int i = 0; i < concurrencyFactor && ranges.hasNext(); i++)
 +            {
 +                concurrentQueries.add(query(ranges.next()));
 +                ++rangesQueried;
 +            }
 +
 +            Tracing.trace("Submitted {} concurrent range requests", concurrentQueries.size());
 +            // We want to count the results for the sake of updating the concurrency factor (see updateConcurrencyFactor) but we don't want to
 +            // enforce any particular limit at this point (this could break code than rely on postReconciliationProcessing), hence the DataLimits.NONE.
 +            counter = DataLimits.NONE.newCounter(command.nowInSec(), true);
 +            return counter.applyTo(PartitionIterators.concat(concurrentQueries));
          }
 -        finally
 +
 +        public void close()
          {
 -            long latency = System.nanoTime() - startTime;
 -            rangeMetrics.addNano(latency);
 -            Keyspace.open(command.keyspace).getColumnFamilyStore(command.columnFamily).metric.coordinatorScanLatency.update(latency, TimeUnit.NANOSECONDS);
 +            try
 +            {
 +                if (sentQueryIterator != null)
 +                    sentQueryIterator.close();
 +            }
 +            finally
 +            {
 +                long latency = System.nanoTime() - startTime;
 +                rangeMetrics.addNano(latency);
 +                Keyspace.openAndGetStore(command.metadata()).metric.coordinatorScanLatency.update(latency, TimeUnit.NANOSECONDS);
 +            }
          }
 -        return command.postReconciliationProcessing(rows);
 +    }
 +
 +    @SuppressWarnings("resource")
 +    public static PartitionIterator getRangeSlice(PartitionRangeReadCommand command, ConsistencyLevel consistencyLevel)
-     throws UnavailableException, ReadFailureException, ReadTimeoutException
 +    {
 +        Tracing.trace("Computing ranges to query");
 +
 +        Keyspace keyspace = Keyspace.open(command.metadata().ksName);
 +        RangeIterator ranges = new RangeIterator(command, keyspace, consistencyLevel);
 +
 +        // our estimate of how many result rows there will be per-range
 +        float resultsPerRange = estimateResultsPerRange(command, keyspace);
 +        // underestimate how many rows we will get per-range in order to increase the likelihood that we'll
 +        // fetch enough rows in the first round
 +        resultsPerRange -= resultsPerRange * CONCURRENT_SUBREQUESTS_MARGIN;
 +        int concurrencyFactor = resultsPerRange == 0.0
 +                              ? 1
 +                              : Math.max(1, Math.min(ranges.rangeCount(), (int) Math.ceil(command.limits().count() / resultsPerRange)));
 +        logger.trace("Estimated result rows per range: {}; requested rows: {}, ranges.size(): {}; concurrent range requests: {}",
 +                     resultsPerRange, command.limits().count(), ranges.rangeCount(), concurrencyFactor);
 +        Tracing.trace("Submitting range requests on {} ranges with a concurrency of {} ({} rows per range expected)", ranges.rangeCount(), concurrencyFactor, resultsPerRange);
 +
 +        // Note that in general, a RangeCommandIterator will honor the command limit for each range, but will not enforce it globally.
 +
 +        return command.limits().filter(command.postReconciliationProcessing(new RangeCommandIterator(ranges, command, concurrencyFactor, keyspace, consistencyLevel)), command.nowInSec());
      }
  
      public Map<String, List<String>> getSchemaVersions()


[26/50] [abbrv] cassandra git commit: Fix clean interval not sent to commit log for empty memtable flush

Posted by ms...@apache.org.
Fix clean interval not sent to commit log for empty memtable flush

patch by Branimir Lambov; reviewed by Jake Luciani for
CASSANDRA-12436


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/ca556c44
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/ca556c44
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/ca556c44

Branch: refs/heads/cassandra-3.8
Commit: ca556c442049808c013bd20649d6611eb4a01a61
Parents: e60f741
Author: Branimir Lambov <br...@datastax.com>
Authored: Thu Aug 11 11:17:37 2016 +0300
Committer: T Jake Luciani <ja...@apache.org>
Committed: Fri Aug 12 11:29:23 2016 -0400

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../apache/cassandra/db/ColumnFamilyStore.java  | 22 +++++------
 .../cassandra/db/commitlog/CommitLog.java       |  5 ++-
 .../db/commitlog/CommitLogSegment.java          |  5 ++-
 .../db/commitlog/CommitLogCQLTest.java          | 41 ++++++++++++++++++++
 5 files changed, 58 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index b1b2050..56875ca 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,6 +6,7 @@
  * cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
  * Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
 Merged from 3.0:
+ * Fix clean interval not sent to commit log for empty memtable flush (CASSANDRA-12436)
  * Fix potential resource leak in RMIServerSocketFactoryImpl (CASSANDRA-12331)
  * Make sure compaction stats are updated when compaction is interrupted (CASSANDRA-12100)
  * Change commitlog and sstables to track dirty and clean intervals (CASSANDRA-11828)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index 21becfe..aa79e90 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -1072,20 +1072,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
             writeBarrier.markBlocking();
             writeBarrier.await();
 
-            // mark all memtables as flushing, removing them from the live memtable list, and
-            // remove any memtables that are already clean from the set we need to flush
-            Iterator<Memtable> iter = memtables.iterator();
-            while (iter.hasNext())
-            {
-                Memtable memtable = iter.next();
+            // mark all memtables as flushing, removing them from the live memtable list
+            for (Memtable memtable : memtables)
                 memtable.cfs.data.markFlushing(memtable);
-                if (memtable.isClean() || truncate)
-                {
-                    memtable.cfs.replaceFlushed(memtable, Collections.emptyList());
-                    reclaim(memtable);
-                    iter.remove();
-                }
-            }
 
             metric.memtableSwitchCount.inc();
 
@@ -1105,6 +1094,13 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
 
         public Collection<SSTableReader> flushMemtable(Memtable memtable)
         {
+            if (memtable.isClean() || truncate)
+            {
+                memtable.cfs.replaceFlushed(memtable, Collections.emptyList());
+                reclaim(memtable);
+                return Collections.emptyList();
+            }
+
             List<Future<SSTableMultiWriter>> futures = new ArrayList<>();
             long totalBytesOnDisk = 0;
             long maxBytesOnDisk = 0;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
index 32f69eb..ce92a0a 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLog.java
@@ -321,8 +321,9 @@ public class CommitLog implements CommitLogMBean
             }
             else
             {
-                logger.trace("Not safe to delete{} commit log segment {}; dirty is {}",
-                        (iter.hasNext() ? "" : " active"), segment, segment.dirtyString());
+                if (logger.isTraceEnabled())
+                    logger.trace("Not safe to delete{} commit log segment {}; dirty is {}",
+                            (iter.hasNext() ? "" : " active"), segment, segment.dirtyString());
             }
 
             // Don't mark or try to delete any newer segments once we've reached the one containing the

http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
index e32c204..94f0ac8 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
@@ -564,7 +564,10 @@ public abstract class CommitLogSegment
         for (UUID cfId : getDirtyCFIDs())
         {
             CFMetaData m = Schema.instance.getCFMetaData(cfId);
-            sb.append(m == null ? "<deleted>" : m.cfName).append(" (").append(cfId).append("), ");
+            sb.append(m == null ? "<deleted>" : m.cfName).append(" (").append(cfId)
+              .append(", dirty: ").append(cfDirty.get(cfId))
+              .append(", clean: ").append(cfClean.get(cfId))
+              .append("), ");
         }
         return sb.toString();
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/ca556c44/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java b/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
new file mode 100644
index 0000000..7235600
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/commitlog/CommitLogCQLTest.java
@@ -0,0 +1,41 @@
+package org.apache.cassandra.db.commitlog;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.junit.Test;
+
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.db.ColumnFamilyStore;
+
+public class CommitLogCQLTest extends CQLTester
+{
+    @Test
+    public void testTruncateSegmentDiscard() throws Throwable
+    {
+        String otherTable = createTable("CREATE TABLE %s (idx INT, data TEXT, PRIMARY KEY(idx));");
+
+        createTable("CREATE TABLE %s (idx INT, data TEXT, PRIMARY KEY(idx));");
+        execute("INSERT INTO %s (idx, data) VALUES (?, ?)", 15, Integer.toString(15));
+        flush();
+
+        // We write something in different table to advance the commit log position. Current table remains clean.
+        executeFormattedQuery(String.format("INSERT INTO %s.%s (idx, data) VALUES (?, ?)", keyspace(), otherTable), 16, Integer.toString(16));
+
+        ColumnFamilyStore cfs = getCurrentColumnFamilyStore();
+        assert cfs.getTracker().getView().getCurrentMemtable().isClean();
+        // Calling switchMemtable directly applies Flush even though memtable is empty. This can happen with some races
+        // (flush with recycling by segment manager). It should still tell commitlog that the memtable's region is clean.
+        // CASSANDRA-12436
+        cfs.switchMemtable();
+
+        execute("INSERT INTO %s (idx, data) VALUES (?, ?)", 15, Integer.toString(17));
+
+        Collection<CommitLogSegment> active = new ArrayList<>(CommitLog.instance.segmentManager.getActiveSegments());
+        CommitLog.instance.forceRecycleAllSegments();
+
+        // If one of the previous segments remains, it wasn't clean.
+        active.retainAll(CommitLog.instance.segmentManager.getActiveSegments());
+        assert active.isEmpty();
+    }
+}


[31/50] [abbrv] cassandra git commit: Wait 10s instead of 1s for connections to be closed on StreamingTransferTest

Posted by ms...@apache.org.
Wait 10s instead of 1s for connections to be closed on StreamingTransferTest


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/315356c1
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/315356c1
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/315356c1

Branch: refs/heads/cassandra-3.8
Commit: 315356c1de7e4d7db5e0f159daebe9a528a8e04d
Parents: 91f7387
Author: Paulo Motta <pa...@gmail.com>
Authored: Fri Aug 12 10:57:29 2016 -0300
Committer: Jason Brown <ja...@gmail.com>
Committed: Sun Aug 14 08:42:58 2016 -0700

----------------------------------------------------------------------
 .../unit/org/apache/cassandra/streaming/StreamingTransferTest.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/315356c1/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
index 2b16267..0af76c2 100644
--- a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
+++ b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
@@ -255,7 +255,7 @@ public class StreamingTransferTest
     {
         //after stream session is finished, message handlers may take several milliseconds to be closed
         outer:
-        for (int i = 0; i <= 10; i++)
+        for (int i = 0; i <= 100; i++)
         {
             for (MessagingService.SocketThread socketThread : MessagingService.instance().getSocketThreads())
                 if (!socketThread.connections.isEmpty())


[38/50] [abbrv] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.9

Posted by ms...@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/de2c449b
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/de2c449b
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/de2c449b

Branch: refs/heads/cassandra-3.8
Commit: de2c449b716e30ef28c9243b48dc1fb47388026c
Parents: e72dc29 6528fbf
Author: Aleksey Yeschenko <al...@apache.org>
Authored: Mon Aug 15 18:32:36 2016 +0100
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Mon Aug 15 18:32:36 2016 +0100

----------------------------------------------------------------------

----------------------------------------------------------------------



[34/50] [abbrv] cassandra git commit: Throw RuntimeException if starting transfer of already completed OutgoingFileMessage

Posted by ms...@apache.org.
Throw RuntimeException if starting transfer of already completed OutgoingFileMessage


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/88b3cfc3
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/88b3cfc3
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/88b3cfc3

Branch: refs/heads/cassandra-3.8
Commit: 88b3cfc3b6b4417a913a512b461897378b48c039
Parents: 315356c
Author: Paulo Motta <pa...@gmail.com>
Authored: Fri Aug 12 22:06:27 2016 -0300
Committer: Yuki Morishita <yu...@apache.org>
Committed: Mon Aug 15 10:09:36 2016 -0500

----------------------------------------------------------------------
 .../apache/cassandra/streaming/messages/OutgoingFileMessage.java  | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/88b3cfc3/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java b/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
index a88386e..30b79f8 100644
--- a/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
+++ b/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java
@@ -115,6 +115,9 @@ public class OutgoingFileMessage extends StreamMessage
     @VisibleForTesting
     public synchronized void startTransfer()
     {
+        if (completed)
+            throw new RuntimeException(String.format("Transfer of file %s already completed or aborted (perhaps session failed?).",
+                                                     filename));
         transferring = true;
     }
 


[21/50] [abbrv] cassandra git commit: Update StorageProxy range metrics for timeouts, failures and unavailables

Posted by ms...@apache.org.
Update StorageProxy range metrics for timeouts, failures and unavailables

patch by Nachiket Patil; reviewed by Alex Petrov for CASSANDRA-9507


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/e86d531e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e86d531e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e86d531e

Branch: refs/heads/cassandra-3.8
Commit: e86d531ec2bf831a68d0bb448e5ed6ad623bbcf7
Parents: 5b38d3a
Author: Nachiket Patil <na...@apple.com>
Authored: Fri Aug 12 15:30:03 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Aug 12 15:30:03 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                          |  1 +
 .../org/apache/cassandra/service/StorageProxy.java   | 15 +++++++++++++++
 2 files changed, 16 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e86d531e/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 14bed81..ddc6720 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.8
+ * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e86d531e/src/java/org/apache/cassandra/service/StorageProxy.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java b/src/java/org/apache/cassandra/service/StorageProxy.java
index 03dd209..7b7979d 100644
--- a/src/java/org/apache/cassandra/service/StorageProxy.java
+++ b/src/java/org/apache/cassandra/service/StorageProxy.java
@@ -1955,6 +1955,21 @@ public class StorageProxy implements StorageProxyMBean
                 }
             }
         }
+        catch (ReadTimeoutException e)
+        {
+            rangeMetrics.timeouts.mark();
+            throw e;
+        }
+        catch (UnavailableException e)
+        {
+            rangeMetrics.unavailables.mark();
+            throw e;
+        }
+        catch (ReadFailureException e)
+        {
+            rangeMetrics.failures.mark();
+            throw e;
+        }
         finally
         {
             long latency = System.nanoTime() - startTime;